- How To ?
- Before We Start
- Conventions & Template
- Git Convention : Strategy
- Git Convention : Commit
- Code Convention
- Project Architecture
- ์ค์น ๋ฐ ์คํ ๋ฐฉ๋ฒ์ ์์ฑํ ๋ถ๋ถ์ ๋๋ค. (์์ )
commit ๋ฐ push ์ ์ npm run format์ ํตํด prettier ํฌ๋งทํ
์ ์ ์ฉํด์ฃผ์ธ์.
Template Repository URL -> Click
made and owned by @kyeoungwoon | Naver Blog
- ๋ธ๋์น ์ข ๋ฅ : main, develop, feature
- MVP ๊ฐ๋ฐ ์๋ฃ ์ ๊น์ง๋ develop branch๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ,
main๊ณผfeature๋ธ๋์น๋ง ์ ์งํฉ๋๋ค.
- Production ํ๊ฒฝ์ ์ธ์ ๋ฐฐํฌํด๋ ๋ฌธ์ ์๋ stable branch ์ ๋๋ค.
- ์ฅ์ ํน์ ๋ฒ๊ทธ ๋ฐ์ ์ main branch๋ฅผ ๊ธฐ์ค์ผ๋ก ๋น ๋ฅด๊ฒ ์์ ํฉ๋๋ค.
- Initial commit์ ์ ์ธํ๊ณ , main branch์ commit์ด ์ง์ ์ ์ผ๋ก ๋ฐ์ํ๋ฉด ์๋ฉ๋๋ค.
- ์๋ก์ด feature๋ค์ ๊ฐ๋ฐํ ๊ฒฝ์ฐ main์ ๊ธฐ์ค์ผ๋ก develop branch๋ฅผ ์์ฑ ํฉ๋๋ค.
- feature branch๋ค์ merge ํ๋ ๊ณณ์ ๋๋ค.
- feature๋ค์ ๋ชจ๋ mergeํ ํ ๋ฐ์๋๋ bug fix๋ฅผ ๋ชจ๋ ๋ง์น ํ, main branch๋ก PR์ ์์ฑํฉ๋๋ค.
- ๋ค์ ํ๋ฒ ๊ฐ์กฐํ์ง๋ง, main branch๋ ๋ชจ๋ ์์ ์ ์์์ ์ด๋ฉฐ, ์ ๋์ ์ผ๋ก stable ํด์ผ ํฉ๋๋ค.
- ๋ถ์์ ํ ์ฌํญ๋ค์ develop branch ๋ด์์ ํด๊ฒฐ๋์ด์ผ ํฉ๋๋ค.
- ์ด ํ๋ก์ ํธ๋ issue๋ฅผ ํตํด branch๋ฅผ ์์ฑํฉ๋๋ค. ๋ฐ๋ผ์ ๋ธ๋์น ๋ช
์ ๋ฐ๋์
feature/{issue๋ฒํธ}-{feature๋ช }๊ณผ ๊ฐ์ ๋ฐฉ์์ด์ฌ์ผ ํฉ๋๋ค. eg.)feature/1-sample - develop branch๋ฅผ ๊ธฐ์ค์ผ๋ก, ์๋ก์ด ๊ธฐ๋ฅ์ ๊ฐ๋ฐํ๋ branch ์ ๋๋ค.
- ์๋ก์ด ๊ธฐ๋ฅ์ ๋ํ bug fix๋ feature branch ๋ด์์ ๋ง์น ํ develop branch๋ก PR์ ์์ฑํด์ผ ํฉ๋๋ค.
์ ๋ชฉ, ๋ณธ๋ฌธ, ๊ผฌ๋ฆฌ๋ง ์ธ ๋ถ๋ถ์ผ๋ก ๋๋ฉ๋๋ค.
๊ฐ ๋ถ๋ถ์ ๋น ์ค๋ก ๊ตฌ๋ถ๋์ด์ผ ํฉ๋๋ค.
- Tag์ ์ฒซ ๋ฌธ์๋ ๋๋ฌธ์๋ก ์์ฑํฉ๋๋ค.
- ์ฝ๋ก ์ Tag์ ๋ถ์ฌ์ ์์ฑํ๊ณ , ์ฝ๋ก ์ดํ 1์นธ ๋ค์ title์ ์์ฑํฉ๋๋ค.
- (X)
feat:titlefeat: titlefeat :titlefeat : titleFeat :titleFeat : title
- (O)
Feat: title
- (X)
Feat: ์๋ก์ด ๊ธฐ๋ฅ์ด ์ถ๊ฐ๋์์ ๋Fix: ๋ฒ๊ทธ๋ฅผ ์์ ํ์์ ๋Docs:README.md๋ ์ฃผ์ ๋ฑ ๋ฌธ์๋ฅผ ์์ ํ์์ ๋Style: ์ฝ๋ ๊ตฌ์กฐ์ ๋ณ๊ฒฝ ์์ด ๋ณ์๋ช ๋ฑ์ ์์ ํ์์ ๋Refactor: ์ฝ๋ ๋์ ๋ฐฉ์์ ์์ ํ์์ ๋, ๋๋ Style์ ๋๊ท๋ชจ๋ก ๋ณ๊ฒฝํ์์ ๋์๋ ํ์ฉ.Test: ํ ์คํธ ์ฝ๋๋ฅผ ์ถ๊ฐํ์์ ๋Chore:package.json์ ์์ ํ์๊ฑฐ๋dockerfile๋ฑ ๋ถ๋ฅํ๊ธฐ ์ ๋งคํ ์ํฉ์์ ์ฌ์ฉMerge: branch๋ฅผ mergeํ์์ ๋ ์ฌ์ฉํฉ๋๋ค.
- ๋ณธ๋ฌธ์ ํ ์ค ๋น 72์ ๋ด๋ก ์์ฑํด ์ฃผ์ธ์. (๋ค์ํ ํ๊ฒฝ์์์ ๊ฐ๋ ์ฑ์ ์ํ์ฌ)
- ๋ณธ๋ฌธ ๋ด์ฉ์ ์์ ๊ตฌ์ ๋ฐ์ง ์๊ณ ์ต๋ํ ์์ธํ ์์ฑํด ์ฃผ์ธ์.
- ๋ณธ๋ฌธ ๋ด์ฉ์ ์ด๋ป๊ฒ ๋ณ๊ฒฝํ๋์ง ๋ณด๋จ, ๋ฌด์์ ๋ณ๊ฒฝํ๋์ง ๋๋ ์ ๋ณ๊ฒฝํ๋์ง๋ฅผ ์ค๋ช ํด ์ฃผ์ธ์.
- Footer๋ ํ์์ ์ด์ง ์์ต๋๋ค.
- ๋ค๋ง, issue์ ์ฐ๊ด๋์ด ์์ฑ๋ commit์ด๋ผ๋ฉด ๋ฃ์ด์ฃผ์๋ ๊ฒ์ ์ถ์ฒํฉ๋๋ค.
์ ๋ชฉ์ ์ธ ๋์ ํ์์ ๋์ผํฉ๋๋ค.- eg.
Fixes: something
- eg.
Fixes: ์ด์ ์์ ์ค (์์ง ํด๊ฒฐ๋์ง ์์ ๊ฒฝ์ฐ)Resolves: ์ด์๋ฅผ ํด๊ฒฐํ์ ๋ ์ฌ์ฉRef: ์ฐธ๊ณ ํ ์ด์๊ฐ ์์ ๋ ์ฌ์ฉRelated to: ํด๋น ์ปค๋ฐ์ ๊ด๋ จ๋ ์ด์๋ฒํธ (์์ง ํด๊ฒฐ๋์ง ์์ ๊ฒฝ์ฐ)
- Camel Case๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- eg.
getUserByUserId,getEventByDateAndUserId
- eg.
- ๊ธธ์ด๊ฐ ๊ธธ์ด์ง๋๋ผ๋ ๊ธฐ๋ฅ์ ๋ช ํํ๊ฒ ๋ช ์ํด ์ฃผ์ธ์.
- ํจ์๋ช
์ ๊ฒน์ณ๋ ๋์ง๋ง, Import/Export์ ์ ์ํด ์ฃผ์ธ์.
- ํ๋จ์ import/export ๊ด๋ จ ์ปจ๋ฒค์ ์ค๋ช ์์ ๋ ์์ธํ ์ ์ ์์ง๋ง, ์๋์ ๊ฐ๋จํ ์์๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์.
user.service.js์createNewUser๊ณผuser.repository.js์createNewUser์ด ๋์์ ์กด์ฌํ์ฌ๋ ๋ฉ๋๋ค.- service์์ ์ฌ์ฉ ์์๋
userRepository.createNewUser - controller์์ ์ฌ์ฉํ ๋๋
userController.createNewUser์ ๊ฐ์ด ์ฌ์ฉํฉ๋๋ค.
- routes ํด๋์์
express.Router()๋ก ์ ์ํ๋ ๋ณ์๋ช ์ router๋ก ํต์ผํฉ๋๋ค.- eg.
const router = express.Router();
- eg.
- ์๋ ์์ธ๋ฅผ ์ ์ธํ ๋ชจ๋ ๊ฒฝ์ฐ์ Camel Case๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- ์์๊ฐ์ ํด๋นํ๋ ๋ณ์๋ช
์ ์ ๋ถ ๋๋ฌธ์ ๋ฐ snake case๋ก ์์ฑ๋์ด์ผ ํฉ๋๋ค.
- ํ๊ฒฝ๋ณ์ ๋ฑ์ด ํด๋นํฉ๋๋ค. eg.
AWS_SECRET_KEY,API_KEY๋ฑ
- ํ๊ฒฝ๋ณ์ ๋ฑ์ด ํด๋นํฉ๋๋ค. eg.
- JSON ๊ฐ์ฒด ์์์๋ Camel Case๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- DB Query๋ฌธ์ ์์ฑํ ๋๋ ORM์ ํตํด Camel Case๋ก ๋ณํํ์ฌ ์ฌ์ฉํ์ ์ผ ํฉ๋๋ค.
- ์ ๋ถ ์๋ฌธ์๋ฅผ ์ฌ์ฉํ์ ์ผ ํฉ๋๋ค.
- ์์ ํด๋๋ช ์ ํฌํจํ์ฌ ๊ธฐ๋ฅ์ ๋ช ์ํด์ผ ํฉ๋๋ค.
- ์ฌ๋ฌ ๋จ์ด๋ฅผ ์ฌ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ . ์ผ๋ก ๋จ์ด๋ฅผ ๊ตฌ๋ถํฉ๋๋ค
- eg.
user.repository.js,chat.service.js
- ํด๋๋ช
์ ๋๋๋ก ํ ๋จ์ด๋ฅผ ์ฌ์ฉํ๋, ๊ธธ์ด์ง ๊ฒฝ์ฐ
-๋ฅผ ํ์ฉํ์ฌ ๊ตฌ๋ถํฉ๋๋ค. (kebab case) - eg.
my-page,user-info
class CustomError extends Error์ ๊ฐ์ด, JavaScript ๊ธฐ๋ณธ Error ๊ฐ์ฒด๋ฅผ extend ํ์ฌ Custom Error๋ฅผ ์์ฑํ์ฌ์ผ ํฉ๋๋ค.- Error๋ ์ธ๋ถํํ์ฌ ๊ฐ๊ฐ ์๋ฌ๋ฅผ ํ ๋นํ๋ ๊ฒ์ด ์๋, ๋๋ถ๋ฅ๋ก ๊ด๋ฆฌํ์ฌ reason์ผ๋ก ์ธ๋ถ์ฌํญ์ ์ ์ ์๋๋ก ํ์ฌ์ผ ํฉ๋๋ค.
- Error ๋ช
์ Pascal Case๋ก ์์ฑํ์ฌ์ผ ํฉ๋๋ค.
UserNotExistErrorUserAuthorizationErrorIdNotProvidedError๋ฑ๊ณผ ๊ฐ์ด ์ธ๋ถํ ๋ ๊ฒ์ด ์๋๋ผ,NotExistErrorInvalidInputError์ฒ๋ผ ์ต๋ํ ํฌ๊ด์ ์ผ๋ก ๊ด๋ฆฌํ์ฌ์ผ ํฉ๋๋ค.- ์ธ๋ถํํ ํ์๊ฐ ์๊ธด๋ค๋ฉด,
UserIdNotExistUserNameNotExist์์ค์ผ๋ก ์ธ๋ถํ ํ๋ ๊ฒ์ด ์๋,InvalidUserDataInput๊ณผ ๊ฐ์ด ์นดํ ๊ณ ๋ฆฌ๊น์ง๋ง ํฌํจํ์ฌ์ผ ํฉ๋๋ค.
- ์์ ์ ์์ ์ด๋, ๋ค์๊ณผ ๊ฐ์ ์ฌํญ์ ํฌํจํ์ฌ์ผ ํฉ๋๋ค.
error codestring์ด์ฌ์ผ ํฉ๋๋ค.U001๊ณผ ๊ฐ์ด ๋ฐ๋ก ์ ์ํด๋์ ๋ ๋๊ณ ,ALREADY_EXIST์ ๊ฐ์ด ํ๋ ๋จ์ด ์ ๋๋ก ๊ฐ๋ตํ๊ฒ ์์ฑํด์ฃผ์ธ์.U001๊ณผ ๊ฐ์ด ์ฝ๋๊ฐ์ผ๋ก ๊ด๋ฆฌํ์ค ์์ ์ด๋ฉด, ๋ฌธ์ํ ํ์ฌ ํ์๋ค ์ฌ์ด์ ๊ณต์ ํด ์ค๋ณต๋ ์๋ฌ๊ฐ ์์ฑ๋์ง ์๋๋ก ์ ์ํด์ฃผ์ธ์.
status code- http status code ๊ฐ ์ ๋๋ค.
- ํด๋น ์๋ฌ๊ฐ ๋ฐ์ํ์ ๋ ์ ์กํ status code๋ฅผ ์ ๋ ฅํด์ฃผ์ธ์.
reason- ํด๋น ์๋ฌ๊ฐ ๋ฐ์ํ ์ด์ ์ ๋๋ค.
- debug ์์ reason๋ง ๋ณด๊ณ ์ ์ ์๋๋ก ๊ฐ๊ฒฐํ๋, ๋ชจ๋ ์ ๋ณด๋ฅผ ํฌํจํ๋๋ก ์์ฑํด ์ฃผ์ธ์.
class AlreadyExistError extends Error {
errorCode = "ALREADY_EXIST";
statusCode = 409;
constructor(reason, data) {
super(reason);
this.reason = reason;
this.data = data;
}
}- ํ ์ด๋ธ๋ช ๊ณผ ์ปฌ๋ผ๋ช ๋ฑ ๋ชจ๋ ๋ณ์๋ช ์ ๋ฐ๋์ snake case๋ฅผ ์ฌ์ฉํ์ฌ์ผ ํฉ๋๋ค. (ERD ์์ฑ ์๋ฅผ ๋งํ๋ ๊ฒ์ด๋ฉฐ, MySQL์ ๋์๋ฌธ์๋ฅผ ๊ตฌ๋ถํ์ง ์์ต๋๋ค.)
- PK๊ฐ์
{table๋ช }_id์ ๊ฐ์ ํ์์ด์ฌ์ผ ํฉ๋๋ค. ๋๋ฌด ๊ธธ์ด์ง ๊ฒฝ์ฐ PK์ธ ๊ฐ์์ ์ ์ ์๋๋ก ์ถ์ฝํ์ฌ ์ฌ์ฉ๋ ๊ฐ๋ฅํฉ๋๋ค.- eg1.
user_oauthํ ์ด๋ธ์ PK ์ปฌ๋ผ ๋ช ์user_oauth_id - eg2. user_profile_images ํ ์ด๋ธ์ PK -> image_id
bigint์๋ฃํ์ ์ฌ์ฉํ๊ณ , auto increment์ ์ฌ์ฉํ์ฌ์ผ ํฉ๋๋ค.- ๋ฐ์ ๊ทํ (๋น์ ๊ทํ, denormailzation)๋ก ์ธํด ํ ์ด๋ธ์ด ๋ถํ ๋ ๊ฒฝ์ฐ์๋, id๊ฐ์ ๋ฐ๋ก ์์ฑํ๋ ๊ฒ์ ์ถ์ฒํฉ๋๋ค.
- eg1.
- ๋ชจ๋ ํ
์ด๋ธ์๋
created_at๊ณผupdated_at์ด ์์ด์ผ ํฉ๋๋ค.- DataType์ TIMESTAMP(6) ์ ๋๋ค.
created_at๊ณผupdated_at์default expression์current_timestamp(6)์ ์ ์ฉํด๋์ด์ผ ํฉ๋๋ค.updated_at์on_update์current_timestamp(6)์ด ์ ์ฉ๋์ด ์์ด์ผ ํฉ๋๋ค.
- ์ ๊ทํ ๊ท์น์ ๋๋๋ก์ด๋ฉด ๋ฐ๋ฅด๋ ๊ฒ์ ์ถ์ฒํฉ๋๋ค.
- ๊ฐ์ ๋ด์ฉ์ Query๋ฅผ ์ฌ๋ฌ๋ฒ ๋ ๋ฆฌ๋ ๊ฒ ๋ณด๋ค๋,
JOIN์ด๋BETWEEN๋ฑ์ผ๋ก ํ๋ฒ์ ๊ฐ์ ธ์์Node.js๋จ์์ ์ฒ๋ฆฌํ๋ ๊ฒ์ ๊ถํฉ๋๋ค. image๋ฑ ํ์ผ์url์ด๋uuid๋ฑ์ ์ ์ฅํ๊ณ ,binary data๋ฅผ ์ง์ ์ ์ฅํ๋ ์ผ์ ํผํด์ฃผ์ธ์.
ES6์ commonJS ๋ชจ๋ ๋์ผํ๊ฒ ์ ์ฉ๋ฉ๋๋ค.
default export์ ์ฌ์ฉ์ ์ง์ํ๊ณ ,named export์ ์ฌ์ฉ์ ์งํฅํฉ๋๋ค.
// ์ง์ : ํจ์๋ฅผ ์ง์ import ํ์ฌ ์ฌ์ฉํ๋ ๊ฒ
import { createNewUser } from "./user.service";
const result = createNewUser();
// user.repository์๋ createNewUser ์กด์ฌํ ๊ฐ๋ฅ์ฑ์ด ์๊ธฐ์, ์ถฉ๋ํ ์ฐ๋ ค๊ฐ ์์ต๋๋ค.
// ์งํฅ : ๋ค์์คํ์ด์ค๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ๋๋ฅผ ๊ตฌ์กฐํ ํฉ๋๋ค.
// eg. Java, C++
import userService from "./sample.service.js";
const result = userService.createNewUser();์ปจ๋ฒค์ ์ ๋ชจ๋ ์งํจ, router - controller - service - repository ๊ฐ์ ํธ์ถ๊ตฌ์กฐ ์์์ ๋๋ค.
// user.router.js
import userController from "./user.controller";
const userRouter = express.Router();
userRouter.get("/", userController.createNewUser);
// user.controller.js
import userService from "./user.service";
export const createNewUser = async (req, res, next) => {
const { id, password } = req.body;
// ์ด๋ ๊ฒ๋ ๋๊ณ
await userService.createNewUser({ id, password }); // RORO
// ์ด๋ ๊ฒ ํด๋ ๋ฉ๋๋ค.
const data = { id, password };
userService.createNewUser(data);
return res.status(200).success();
};
// user.service.js
import userRepository from "./user.repository";
export const createNewUser = async (data) => {
const result = await userRepository.createNewUser(data);
return { message: "OK", result };
};
// user.repository.js
export const createNewUser = async (data) => {
const result = await User.create(data);
return result;
};- ์๋ํฌ์ธํธ๋ณ๋ก controller๋ฅผ ๋ฌถ๋ ์ญํ ์ ํ๋ router ํ์ผ๋ค์ ์์ฑํฉ๋๋ค.
- /routes/index.js ์์ ๋ชจ๋ router๋ค์ ๋ชจ์์ export ํฉ๋๋ค.
- ๊ฐ ์๋ํฌ์ธํธ์ ์๋ต์ ํธ๋ค๋งํฉ๋๋ค.
- try - catch๋ฅผ ํตํด catchํ error๋ฅผ next๋ฅผ ์ด์ฉํด index.js (๊ฐ์ฅ ์๋จ) ์์ญ์ผ๋ก ์๋ฌ ํธ๋ค๋ง์ ์ญํ ์ ๋๊ฒจ์ผ ํฉ๋๋ค.
- service์ ์์กด์ ์ ๋๋ค.
controllers์์ ํ์ฉํ ๊ธฐ๋ฅ ๋ฑ์ ์ํ ํด๋์ ๋๋ค.- ์์กด์ฑ์ ๊ฐ์ง์ง ์์ผ๋ฉฐ, DB์ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฌ๋ ์์ ๋ํ service๋ก ๋ถ๋ฅ๋ฉ๋๋ค.
- ์ ๋ ฅ๊ฐ validation์ด๋ error handling ๋ฑ์ ๋ด๋นํฉ๋๋ค.
- Seqelize์ model ์ ์๋ฅผ ์ํ ํด๋์ ๋๋ค.
- /models/index.js ์์ model๋ก named export ๋๋ ๊ฐ์ฒด๋ฅผ import ํ์ฌ ๊ทธ ์์ ์๋ Sequelize Model ๊ฐ์ฒด๋ฅผ ํ์ฉํฉ๋๋ค.