diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..0b93d25 Binary files /dev/null and b/.DS_Store differ diff --git a/Unit6/unit6.md b/Unit6/unit6.md new file mode 100644 index 0000000..904dccb --- /dev/null +++ b/Unit6/unit6.md @@ -0,0 +1,42 @@ +### ⭐️ 6주차 과제 제출 ⭐️ + +## ❗️ 강의 수강 여부 +수강한 강의에 체크표시 해주세요~ + +- [ ] HTTP 통신 +- [ ] API +- [ ] 데이터 포맷 - CSV, JSON, XML + +
+ +## ❗️ notion에 키워드 정리 완료 여부 +내가 notion에 키워드를 정리를 완료한 경우 체크표시 해주세요~ + +- [ ] 키워드 정리 완료 + +
+ +## ❗️ 6주차 과제 +1. 개발환경 구축하기 - nodejs 설치 및 사전에 올려둔 템플릿 적용하기 + (완료된 내용 켬쳐본 올리기) + +
+ +2. 자신이 설계한 DB와 연동해서 API설계 (CRUD) + (완료된 내용 켬쳐본 올리기) + +
+ +3. Postman으로 API 테스트 + (완료된 내용 켬쳐본 올리기) + +
+ +4. API Sheet 작성 + (완료된 내용 켬쳐본 올리기) + + +
+ + + diff --git a/Unit7/unit7.md b/Unit7/unit7.md new file mode 100644 index 0000000..f701a84 --- /dev/null +++ b/Unit7/unit7.md @@ -0,0 +1,33 @@ +### ⭐️ 7주차 과제 제출 ⭐️ + +## ❗️ 강의 수강 여부 +수강한 강의에 체크표시 해주세요~ + +- [ ] Restful API +- [ ] Path Variable, Query Parameter + +
+ +## ❗️ notion에 키워드 정리 완료 여부 +내가 notion에 키워드를 정리를 완료한 경우 체크표시 해주세요~ + +- [ ] 키워드 정리 완료 + +
+ +## ❗️ 7주차 과제 +1. GET 메소드 함수 작성 완료하기 + (fork에 코드 올린 후 PR 날리기) + +
+ +2. test 진행하기(postman 이용하기) + (완료된 내용 켬쳐본 올리기) + +
+ +3. API Sheet 작성 + (완료된 내용 켬쳐본 올리기) + + +
diff --git a/server_nodejs_templete/.gitignore b/server_nodejs_templete/.gitignore new file mode 100644 index 0000000..68d752c --- /dev/null +++ b/server_nodejs_templete/.gitignore @@ -0,0 +1,5 @@ +.idea/* +node_modules/* +package-lock.json +log/* +.DS_Store \ No newline at end of file diff --git a/server_nodejs_templete/README.md b/server_nodejs_templete/README.md new file mode 100644 index 0000000..22bc396 --- /dev/null +++ b/server_nodejs_templete/README.md @@ -0,0 +1 @@ +# Node.js Template diff --git a/server_nodejs_templete/config/baseResponseStatus.js b/server_nodejs_templete/config/baseResponseStatus.js new file mode 100644 index 0000000..77e5769 --- /dev/null +++ b/server_nodejs_templete/config/baseResponseStatus.js @@ -0,0 +1,49 @@ +module.exports = { + + // Success + SUCCESS : { "isSuccess": true, "code": 1000, "message":"성공" }, + + // Common + TOKEN_EMPTY : { "isSuccess": false, "code": 2000, "message":"JWT 토큰을 입력해주세요." }, + TOKEN_VERIFICATION_FAILURE : { "isSuccess": false, "code": 3000, "message":"JWT 토큰 검증 실패" }, + TOKEN_VERIFICATION_SUCCESS : { "isSuccess": true, "code": 1001, "message":"JWT 토큰 검증 성공" }, // ? + + //Request error + SIGNUP_EMAIL_EMPTY : { "isSuccess": false, "code": 2001, "message":"이메일을 입력해주세요" }, + SIGNUP_EMAIL_LENGTH : { "isSuccess": false, "code": 2002, "message":"이메일은 30자리 미만으로 입력해주세요." }, + SIGNUP_EMAIL_ERROR_TYPE : { "isSuccess": false, "code": 2003, "message":"이메일을 형식을 정확하게 입력해주세요." }, + SIGNUP_PASSWORD_EMPTY : { "isSuccess": false, "code": 2004, "message": "비밀번호를 입력 해주세요." }, + SIGNUP_PASSWORD_LENGTH : { "isSuccess": false, "code": 2005, "message":"비밀번호는 6~20자리를 입력해주세요." }, + SIGNUP_NICKNAME_EMPTY : { "isSuccess": false, "code": 2006, "message":"닉네임을 입력 해주세요." }, + SIGNUP_NICKNAME_LENGTH : { "isSuccess": false,"code": 2007,"message":"닉네임은 최대 20자리를 입력해주세요." }, + + SIGNIN_EMAIL_EMPTY : { "isSuccess": false, "code": 2008, "message":"이메일을 입력해주세요" }, + SIGNIN_EMAIL_LENGTH : { "isSuccess": false, "code": 2009, "message":"이메일은 30자리 미만으로 입력해주세요." }, + SIGNIN_EMAIL_ERROR_TYPE : { "isSuccess": false, "code": 2010, "message":"이메일을 형식을 정확하게 입력해주세요." }, + SIGNIN_PASSWORD_EMPTY : { "isSuccess": false, "code": 2011, "message": "비밀번호를 입력 해주세요." }, + + USER_USERID_EMPTY : { "isSuccess": false, "code": 2012, "message": "userId를 입력해주세요." }, + USER_USERID_NOT_EXIST : { "isSuccess": false, "code": 2013, "message": "해당 회원이 존재하지 않습니다." }, + + USER_USEREMAIL_EMPTY : { "isSuccess": false, "code": 2014, "message": "이메일을 입력해주세요." }, + USER_USEREMAIL_NOT_EXIST : { "isSuccess": false, "code": 2015, "message": "해당 이메일을 가진 회원이 존재하지 않습니다." }, + USER_ID_NOT_MATCH : { "isSuccess": false, "code": 2016, "message": "유저 아이디 값을 확인해주세요" }, + USER_NICKNAME_EMPTY : { "isSuccess": false, "code": 2017, "message": "변경할 닉네임 값을 입력해주세요" }, + + USER_STATUS_EMPTY : { "isSuccess": false, "code": 2018, "message": "회원 상태값을 입력해주세요" }, + + // Response error + SIGNUP_REDUNDANT_EMAIL : { "isSuccess": false, "code": 3001, "message":"중복된 이메일입니다." }, + SIGNUP_REDUNDANT_NICKNAME : { "isSuccess": false, "code": 3002, "message":"중복된 닉네임입니다." }, + + SIGNIN_EMAIL_WRONG : { "isSuccess": false, "code": 3003, "message": "아이디가 잘못 되었습니다." }, + SIGNIN_PASSWORD_WRONG : { "isSuccess": false, "code": 3004, "message": "비밀번호가 잘못 되었습니다." }, + SIGNIN_INACTIVE_ACCOUNT : { "isSuccess": false, "code": 3005, "message": "비활성화 된 계정입니다. 고객센터에 문의해주세요." }, + SIGNIN_WITHDRAWAL_ACCOUNT : { "isSuccess": false, "code": 3006, "message": "탈퇴 된 계정입니다. 고객센터에 문의해주세요." }, + + //Connection, Transaction 등의 서버 오류 + DB_ERROR : { "isSuccess": false, "code": 4000, "message": "데이터 베이스 에러"}, + SERVER_ERROR : { "isSuccess": false, "code": 4001, "message": "서버 에러"}, + + +} diff --git a/server_nodejs_templete/config/database.js b/server_nodejs_templete/config/database.js new file mode 100644 index 0000000..03146d0 --- /dev/null +++ b/server_nodejs_templete/config/database.js @@ -0,0 +1,15 @@ +const mysql = require('mysql2/promise'); +const {logger} = require('./winston'); + +// TODO: 본인의 DB 계정 입력 +const pool = mysql.createPool({ + host: '', + user: '', + port: '3306', + password: '', + database: '' +}); + +module.exports = { + pool: pool +}; \ No newline at end of file diff --git a/server_nodejs_templete/config/express.js b/server_nodejs_templete/config/express.js new file mode 100644 index 0000000..a7608c6 --- /dev/null +++ b/server_nodejs_templete/config/express.js @@ -0,0 +1,24 @@ +const express = require('express'); +const compression = require('compression'); +const methodOverride = require('method-override'); +var cors = require('cors'); +module.exports = function () { + const app = express(); + + app.use(compression()); + + app.use(express.json()); + + app.use(express.urlencoded({extended: true})); + + app.use(methodOverride()); + + app.use(cors()); + // app.use(express.static(process.cwd() + '/public')); + + /* App (Android, iOS) */ + // 도메인을 추가할 경우 이곳에 Route를 추가 + require('../src/app/User/userRoute')(app); + + return app; +}; \ No newline at end of file diff --git a/server_nodejs_templete/config/jwtMiddleware.js b/server_nodejs_templete/config/jwtMiddleware.js new file mode 100644 index 0000000..92b2d14 --- /dev/null +++ b/server_nodejs_templete/config/jwtMiddleware.js @@ -0,0 +1,38 @@ +const jwt = require('jsonwebtoken'); +const secret_config = require('./secret'); +const { response } = require("./response") +const { errResponse } = require("./response") +const baseResponse = require("./baseResponseStatus"); + + +const jwtMiddleware = (req, res, next) => { + // read the token from header or url + const token = req.headers['x-access-token'] || req.query.token; + // token does not exist + if(!token) { + return res.send(errResponse(baseResponse.TOKEN_EMPTY)) + } + + // create a promise that decodes the token + const p = new Promise( + (resolve, reject) => { + jwt.verify(token, secret_config.jwtsecret , (err, verifiedToken) => { + if(err) reject(err); + resolve(verifiedToken) + }) + } + ); + + // if it has failed to verify, it will return an error message + const onError = (error) => { + return res.send(errResponse(baseResponse.TOKEN_VERIFICATION_FAILURE)) + }; + // process the promise + p.then((verifiedToken)=>{ + //비밀 번호 바뀌었을 때 검증 부분 추가 할 곳 + req.verifiedToken = verifiedToken; + next(); + }).catch(onError) +}; + +module.exports = jwtMiddleware; \ No newline at end of file diff --git a/server_nodejs_templete/config/response.js b/server_nodejs_templete/config/response.js new file mode 100644 index 0000000..8992a22 --- /dev/null +++ b/server_nodejs_templete/config/response.js @@ -0,0 +1,18 @@ +const response = ({isSuccess, code, message}, result) => { + return { + isSuccess: isSuccess, + code: code, + message: message, + result: result + } + }; + + const errResponse = ({isSuccess, code, message}) => { + return { + isSuccess: isSuccess, + code: code, + message: message + } + }; + + module.exports = { response, errResponse }; \ No newline at end of file diff --git a/server_nodejs_templete/config/secret.js b/server_nodejs_templete/config/secret.js new file mode 100644 index 0000000..bef208e --- /dev/null +++ b/server_nodejs_templete/config/secret.js @@ -0,0 +1,5 @@ +// 해당 KEY 값들을 꼭 바꿔서 사용하기! +// 반드시 .gitignore에 추가하기! +module.exports = { + 'jwtsecret' : '', +}; \ No newline at end of file diff --git a/server_nodejs_templete/config/winston.js b/server_nodejs_templete/config/winston.js new file mode 100644 index 0000000..c01031e --- /dev/null +++ b/server_nodejs_templete/config/winston.js @@ -0,0 +1,48 @@ +const { createLogger, format, transports } = require('winston'); +require('winston-daily-rotate-file'); +const fs = require('fs'); + +const env = process.env.NODE_ENV || 'development'; +const logDir = 'log'; + +// Javascript winston logger 이용하기 +// https://lovemewithoutall.github.io/it/winston-example/ +// Create the log directory if it does not exist +if (!fs.existsSync(logDir)) { + fs.mkdirSync(logDir) +} + +const dailyRotateFileTransport = new transports.DailyRotateFile({ + level: 'debug', + filename: `${logDir}/%DATE%-smart-push.log`, + datePattern: 'YYYY-MM-DD', + zippedArchive: true, + maxSize: '20m', + maxFiles: '14d' +}); + +const logger = createLogger({ + level: env === 'development' ? 'debug' : 'info', + format: format.combine( + format.timestamp({ + format: 'YYYY-MM-DD HH:mm:ss' + }), + format.json() + ), + transports: [ + new transports.Console({ + level: 'info', + format: format.combine( + format.colorize(), + format.printf( + info => `${info.timestamp} ${info.level}: ${info.message}` + ) + ) + }), + dailyRotateFileTransport + ] +}); + +module.exports = { + logger: logger +}; \ No newline at end of file diff --git a/server_nodejs_templete/index.js b/server_nodejs_templete/index.js new file mode 100644 index 0000000..3f8c580 --- /dev/null +++ b/server_nodejs_templete/index.js @@ -0,0 +1,6 @@ +const express = require('./config/express'); +const {logger} = require('./config/winston'); + +const port = 3000; +express().listen(port); +logger.info(`${process.env.NODE_ENV} - API Server Start At Port ${port}`); \ No newline at end of file diff --git a/server_nodejs_templete/package.json b/server_nodejs_templete/package.json new file mode 100644 index 0000000..584eaba --- /dev/null +++ b/server_nodejs_templete/package.json @@ -0,0 +1,30 @@ +{ + "name": "api-server-node", + "version": "1.0.0", + "description": "Node.js API Server", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "NODE_ENV=development node index.js", + "prod": "NODE_ENV=production node index.js" + }, + "repository": { + "type": "git", + "url": "git@gitlab.com:softsquared/template/api-server-node.git" + }, + "author": "", + "license": "ISC", + "dependencies": { + "compression": "^1.7.4", + "cors": "^2.8.5", + "crypto": "^1.0.1", + "express": "^4.17.1", + "jsonwebtoken": "^8.5.1", + "method-override": "^3.0.0", + "mysql2": "^2.0.0", + "nodemon": "^2.0.7", + "regex-email": "^1.0.2", + "winston": "^3.2.1", + "winston-daily-rotate-file": "^4.2.1" + } +} diff --git a/server_nodejs_templete/src/app/User/userController.js b/server_nodejs_templete/src/app/User/userController.js new file mode 100644 index 0000000..7c92afc --- /dev/null +++ b/server_nodejs_templete/src/app/User/userController.js @@ -0,0 +1,17 @@ +const jwtMiddleware = require("../../../config/jwtMiddleware"); +const userProvider = require("../../app/User/userProvider"); +const userService = require("../../app/User/userService"); +const baseResponse = require("../../../config/baseResponseStatus"); +const {response, errResponse} = require("../../../config/response"); + +const regexEmail = require("regex-email"); +const {emit} = require("nodemon"); + +/** + * API No. 0 + * API Name : 테스트 API + * [GET] /app/test + */ + exports.getTest = async function (req, res) { + return res.send(response(baseResponse.SUCCESS)) + }; \ No newline at end of file diff --git a/server_nodejs_templete/src/app/User/userDao.js b/server_nodejs_templete/src/app/User/userDao.js new file mode 100644 index 0000000..e69de29 diff --git a/server_nodejs_templete/src/app/User/userProvider.js b/server_nodejs_templete/src/app/User/userProvider.js new file mode 100644 index 0000000..d510e3b --- /dev/null +++ b/server_nodejs_templete/src/app/User/userProvider.js @@ -0,0 +1,4 @@ +const { pool } = require("../../../config/database"); +const { logger } = require("../../../config/winston"); + +const userDao = require("./userDao"); diff --git a/server_nodejs_templete/src/app/User/userRoute.js b/server_nodejs_templete/src/app/User/userRoute.js new file mode 100644 index 0000000..38f8d89 --- /dev/null +++ b/server_nodejs_templete/src/app/User/userRoute.js @@ -0,0 +1,8 @@ +module.exports = function(app){ + const user = require('./userController'); + const jwtMiddleware = require('../../../config/jwtMiddleware'); + + // 0. 테스트 API + app.get('/app/test', user.getTest) + +}; diff --git a/server_nodejs_templete/src/app/User/userService.js b/server_nodejs_templete/src/app/User/userService.js new file mode 100644 index 0000000..a2a83fb --- /dev/null +++ b/server_nodejs_templete/src/app/User/userService.js @@ -0,0 +1,15 @@ +const {logger} = require("../../../config/winston"); +const {pool} = require("../../../config/database"); +const secret_config = require("../../../config/secret"); +const userProvider = require("./userProvider"); +const userDao = require("./userDao"); +const baseResponse = require("../../../config/baseResponseStatus"); +const {response} = require("../../../config/response"); +const {errResponse} = require("../../../config/response"); + +const jwt = require("jsonwebtoken"); +const crypto = require("crypto"); +const {connect} = require("http2"); + +// Service: Create, Update, Delete 비즈니스 로직 처리 +