You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
REST API 서버 구축 및 비즈니스 로직 처리 (TypeORM, JWT 인증, Swagger 문서화)
App Server
EC2 리버스 프록시 설정 및 요청 크기 제한 관리
Database
TypeORM 기반 데이터 관리 및 마이그레이션
Compute
EC2 인스턴스 기반 애플리케이션 호스팅 및 PM2 프로세스 관리
Storage
일기·프로필 이미지 업로드 및 객체 URL 관리
CI/CD
main 브랜치 Push 시 자동 빌드·검증 및 EC2 자동 배포
🏗 Service Architecture Flow
📁 시스템 디렉토리 구조
NestJS는 기능(도메인) 단위로 모듈을 나눠서 관리합니다. controllers/, services/, dtos/ 처럼 역할별로 폴더를 나누는 계층형 구조 대신, 도메인 하나에 필요한 controller, service, dto, entity를 같은 폴더 안에 모아두는 도메인 단위 구조를 사용합니다.
이슈 생성 → GitHub Issue 탭에서 작업할 내용 등록 (이슈명: [Feat/Fix/Chore/Refactor] 이슈 명) → Assignees 본인 지정
Branch 생성 → develop에서 분기하여 작업 브랜치 생성
작업 및 Push → 컨벤션에 맞춰 커밋 진행 후 원격 저장소에 push
Pull Request 생성 → 작업 브랜치 → develop으로 PR 생성, 템플릿에 맞춰 작성
Review & Merge → 팀원 리뷰 후 develop에 병합
▷ Branch 전략
브랜치명
설명
명명 규칙 예시
main
실제 배포되어 운영되는 서버의 코드
main
develop
다음 배포를 위해 개발된 기능들이 통합되는 브랜치
develop
feature
단위 기능 개발을 위한 브랜치, develop에서 분기
feature/이슈-번호
브랜치명 형식: <타입>/#<이슈번호>
예시: feat/#12, fix/#23
▷ 이슈 유형 & 커밋 컨벤션
타입
설명
커밋 예시
Feat
새로운 기능 추가
Feat: 카카오 로그인 기능 구현
Fix
오류/버그 수정
Fix: 세부과제 상태 변경 시 권한 체크 오류 수정
Chore
빌드 설정, CI/CD, 라이브러리 변경
Chore: GitHub Actions CD 배포 설정
Docs
문서 수정 (README, API 명세 등)
Docs: API 엔드포인트 명세 업데이트
Refactor
기능 변경 없는 코드 구조 개선
Refactor: 알림 서비스 로직 개선
커밋 메시지 규칙
형식: <타입>: 제목
세미콜론(;)으로 문장 종료, 문자열은 작은따옴표 사용
하나의 작업 단위별로 커밋 (너무 크거나 작은 커밋 지양)
▷ 브랜치 생성 및 작업 방법 (Git CLI)
# 1. develop 브랜치로 이동 및 최신화
git checkout develop
git pull origin develop
# 2. 작업 브랜치 생성
git checkout -b <타입>/#이슈번호# 3. 작업 후 커밋
git add .
git commit -m "<타입>: 내용"# 4. 원격 저장소 push
git push origin <타입>/#이슈번호
개인 브랜치 작업 전에는 항상 develop을 최신화하여 Conflict를 최소화한다.
PR을 올린 상태에서 추가 작업이 필요한 경우 커밋 제목 앞에 [WIP]를 붙인다. (예: [WIP]Feat/#12] 카카오 로그인 로직 구현)
▷ Pull Request 작성 방법
작업 브랜치를 push한 후 GitHub에서 develop으로 PR 생성
제목 형식: [타입/#이슈번호] 작업 내용 (예: [Feat/#12] 카카오 로그인 기능 구현)
Assignees: PR 작성자 본인 지정 / Reviewer는 별도 지정하지 않음
본문 템플릿
### 📌 관련 이슈번호- Closes #이슈번호
### 📌 PR 유형-[ ] 새 기능 추가
-[ ] 버그 수정
-[ ] 리팩토링
-[ ] 배포/설정 변경
### 📌 PR 요약
해당 PR을 간단하게 요약해 주세요
### 📌 작업 세부 내용1.2.3.### 📸 스크린샷 (선택)### 🔗 참고 자료
주의사항
PR은 작고 명확한 단위로 만들어 리뷰 부담을 줄인다.
충분한 테스트를 거친 후 PR을 생성한다.
리뷰는 CodeRabbit을 통해 1차 검토를 진행하며, 지적된 사항은 확인 후 반영한다.
Closes #이슈번호를 반드시 포함하여 머지 시 이슈가 자동으로 닫히도록 한다.
⚙️ API 설계
▷ 공통 응답 포맷
// 성공 시
{
"resultType": "SUCCESS",
"message": "string",
"data": {
...
}
}
// 실패 시
{
"resultType": "FAIL",
"code": 500,
"errorCode": "INTERNAL_SERVER_ERROR",
"reason": "서버 내부 오류가 발생했습니다",
"data": null
}
▷ HTTP 상태 코드
코드
상태 텍스트
의미
200
OK
서버가 요청을 성공적으로 처리
201
Created
요청이 처리되어 새로운 리소스가 생성됨
400
Bad Request
요청의 구문/파라미터가 잘못됨
401
Unauthorized
인증에 실패함 (토큰 없음/만료)
403
Forbidden
인증은 되었으나 해당 리소스에 대한 접근 권한 없음
404
Not Found
지정한 리소스를 찾을 수 없음
409
Conflict
요청 처리 중 리소스 상태 충돌 발생 (중복 등)
500
Internal Server Error
서버 내부 오류 발생
▷ 커스텀 에러 클래스 구조
CustomError를 상속받아 도메인 공통으로 사용하는 에러 클래스를 정의한다. 서비스 레이어에서는 try/catch 없이 throw만 하고, 공통 Exception Filter/Middleware가 이를 받아 정해진 응답 포맷으로 변환한다.
// 기본 커스텀 에러 클래스classCustomErrorextendsError{constructor(statusCode,errorCode,reason,data=null){super(reason);this.statusCode=statusCode;this.errorCode=errorCode;this.reason=reason;this.data=data;this.name=this.constructor.name;}}// 400 Bad RequestclassBadRequestErrorextendsCustomError{constructor(errorCode='BAD_REQUEST',reason='잘못된 요청입니다',data=null){super(400,errorCode,reason,data);}}// 401 UnauthorizedclassUnauthorizedErrorextendsCustomError{constructor(errorCode='UNAUTHORIZED',reason='인증에 실패했습니다',data=null){super(401,errorCode,reason,data);}}// 403 ForbiddenclassForbiddenErrorextendsCustomError{constructor(errorCode='FORBIDDEN',reason='접근 권한이 없습니다',data=null){super(403,errorCode,reason,data);}}// 404 Not FoundclassNotFoundErrorextendsCustomError{constructor(errorCode='NOT_FOUND',reason='리소스를 찾을 수 없습니다',data=null){super(404,errorCode,reason,data);}}// 409 ConflictclassConflictErrorextendsCustomError{constructor(errorCode='CONFLICT',reason='요청이 리소스 상태와 충돌합니다',data=null){super(409,errorCode,reason,data);}}// 500 Internal Server ErrorclassInternalServerErrorextendsCustomError{constructor(errorCode='INTERNAL_SERVER_ERROR',reason='서버 내부 오류가 발생했습니다',data=null){super(500,errorCode,reason,data);}}module.exports={
CustomError,
BadRequestError,
UnauthorizedError,
ForbiddenError,
NotFoundError,
ConflictError,
InternalServerError,};
▷ 에러 처리 규칙
규칙
내용
검증 위치
필수값 존재 여부는 컨트롤러(DTO)에서, 비즈니스 규칙 검증(중복, 권한, 상태 불일치 등)은 서비스에서
에러 발생 방식
errors/ 커스텀 에러 클래스를 그대로 throw, try/catch로 감싸지 않음
공통 처리
공통 에러 핸들링 미들웨어가 statusCode/errorCode/reason을 받아 위 실패 응답 포맷으로 변환
에러 코드 네이밍
<도메인>_<상황> 형태의 스네이크 케이스 사용 (예: TASK_NOT_FOUND, USER_NICKNAME_DUPLICATED, ALARM_NOT_FOUND)