TIL - 20260902

juni·2026년 9월 2일

TIL

목록 보기
447/468

0902 백엔드 아키텍처 고도화 (10/N): 백엔드 아키텍처 운영 체크리스트


✅ 1. 백엔드 아키텍처 고도화 마무리

  • 백엔드 아키텍처 고도화는 단순히 폴더 구조를 예쁘게 만드는 작업이 아닙니다.
  • 실제 운영 서비스에서 기능이 늘어나도 코드가 무너지지 않고, 장애가 났을 때 원인을 추적할 수 있고, AI/Codex에게 작업을 맡겨도 영향 범위를 통제할 수 있게 만드는 기준입니다.
  • 특히 현재 프로젝트처럼 1인 개발자가 고객 화면, 관리자 화면, DB, AWS, 배포, 알림, 엑셀, 유입 분석까지 관리하는 구조에서는 아키텍처가 곧 운영 안전장치입니다.
Domain Service / Use Case 분리
  ↓
Repository Pattern과 Prisma 의존성 관리
  ↓
Transaction Boundary와 상태 변경 Use Case
  ↓
Queue/Worker, ExportJob과 알림톡 구조
  ↓
Webhook, 외부 API 연동과 재시도 전략
  ↓
Permission Policy, 관리자 권한과 접근 제어
  ↓
Audit Log, Event Log와 운영 추적성
  ↓
Error Handling, Exception Filter와 표준 응답 구조
  ↓
테스트 가능한 구조와 Mock 전략
  ↓
백엔드 아키텍처 운영 체크리스트

➕ 1-1. 백엔드 아키텍처를 잘 잡는다는 것

단순히:
코드가 동작한다

실무적으로:
기능별 책임이 분리되어 있다
중요 변경은 transaction으로 보호된다
관리자 권한이 명확하다
외부 API 실패가 서비스 전체 장애로 번지지 않는다
Audit Log로 운영 작업을 추적할 수 있다
에러 응답이 표준화되어 있다
테스트로 핵심 흐름을 검증할 수 있다
  • 아키텍처의 목적은 복잡한 이론을 적용하는 것이 아닙니다.
  • 실무에서 사고를 줄이고, 수정 속도를 높이고, 운영 품질을 유지하는 것입니다.

✅ 2. 지금까지 다룬 백엔드 아키텍처 주제 요약

➕ 2-1. 0824 Domain Service, Use Case와 비즈니스 로직 분리

핵심:
Controller는 요청/응답 처리
Use Case는 업무 흐름 조합
Domain Service는 핵심 규칙 관리
Repository는 DB 접근 담당
Adapter는 외부 API 연동 담당
  • 상담 신청, 상담 상태 변경, 엑셀 Export 요청처럼 복잡한 흐름은 Use Case로 분리하는 것이 좋습니다.
  • 상태 전이, 중복 신청, 권한 정책 같은 규칙은 Domain Service/Policy로 빼야 재사용과 테스트가 쉬워집니다.

➕ 2-2. 0825 Repository Pattern과 Prisma 의존성 관리

핵심:
Prisma query를 여기저기 흩뿌리지 않기
ConsultRepository, ProductRepository, AuditLogRepository로 분리
Repository는 tx를 선택적으로 받을 수 있게 설계
Soft Delete 조건과 select 기준 통일
  • Repository는 DB 접근 로직을 모으는 계층입니다.
  • 비즈니스 규칙을 Repository에 넣기보다 Use Case/Domain Service에서 관리하는 것이 좋습니다.

➕ 2-3. 0826 Transaction Boundary와 상태 변경 Use Case 설계

핵심:
transaction boundary는 Use Case에 둔다
상태 변경 + 상태 이력 + Audit Log는 같은 transaction
외부 API 호출은 transaction 밖으로 분리
동시 수정은 409 Conflict로 처리
  • 상담 상태 변경은 단순 update가 아닙니다.
  • 현재 상태 확인, 권한 확인, 상태 전이 검증, 상태 업데이트, 상태 이력, Audit Log까지 하나의 업무 흐름으로 설계해야 합니다.

➕ 2-4. 0827 Queue/Worker, ExportJob과 알림톡 발송 구조

핵심:
오래 걸리는 작업은 API 요청에서 분리
ExportJob, NotificationJob으로 작업 상태 관리
Worker가 실제 파일 생성/알림 발송 처리
retryCount, errorCode, startedAt, finishedAt 기록
  • 엑셀 생성, 알림톡 발송, S3 업로드 같은 작업은 API 안에서 바로 처리하면 위험합니다.
  • DB Job + Worker 구조로 시작하고, 처리량이 커지면 BullMQ/Redis를 검토하는 흐름이 현실적입니다.

➕ 2-5. 0828 Webhook, 외부 API 연동과 재시도 전략

핵심:
외부 API 호출은 Adapter로 분리
timeout, retry/backoff, idempotency 필요
Webhook은 signature 검증과 providerEventId unique 필요
Webhook 수신과 실제 처리는 분리
  • 외부 API는 언제든 실패할 수 있습니다.
  • 실패를 정상 상황으로 보고 timeout, 에러 분류, 재시도, 중복 처리, 로그 기준을 설계해야 합니다.

➕ 2-6. 0829 Permission Policy, 관리자 권한과 접근 제어

핵심:
인증과 인가 구분
Role보다 PermissionCode 기준으로 판단
RequirePermissions decorator와 PermissionGuard 적용
위험 작업은 별도 권한으로 분리
프론트 숨김은 UX, 백엔드 검증은 보안
  • 엑셀 다운로드, 상품/지원금 수정, 관리자 계정 관리, Audit Log 조회는 특히 민감합니다.
  • 조회 권한과 다운로드 권한은 분리해야 합니다.

➕ 2-7. 0830 Audit Log, Event Log와 운영 추적성

핵심:
Audit Log는 관리자/시스템 행위 추적
Event Log는 도메인 사건 기록
requestId로 API 로그, Job, Audit Log 연결
beforeValue/afterValue에는 변경 필드만 최소 저장
  • 운영 추적성은 장애가 난 뒤에 만들 수 없습니다.
  • 상품 가격 변경, 상담 상태 변경, 엑셀 다운로드, 관리자 권한 변경은 반드시 기록 대상으로 보는 것이 좋습니다.

➕ 2-8. 0831 Error Handling, Exception Filter와 표준 응답 구조

핵심:
모든 에러 응답 구조 통일
error.code, message, details, requestId 포함
GlobalExceptionFilter 적용
Prisma/외부 API/도메인 에러 구분
민감정보와 stack trace 응답 금지
  • 에러 응답이 표준화되면 프론트 공통 처리와 장애 대응이 쉬워집니다.
  • 401, 403, 404, 409, 500을 구분하고 관리자에게 조치 가능한 메시지를 제공해야 합니다.

➕ 2-9. 0901 테스트 가능한 백엔드 구조와 Mock 전략

핵심:
Domain Service는 Unit Test
Use Case + Repository는 Integration Test
Controller/Guard/응답 구조는 E2E Test
외부 API/S3/시간/랜덤값은 Mock
운영 DB와 테스트 DB 절대 분리
  • 테스트는 AI/Codex 리팩토링의 안전장치입니다.
  • 상담 상태 변경, 권한, ExportJob, NotificationJob, 표준 에러 응답부터 테스트 가치가 높습니다.

✅ 3. 현재 프로젝트 기준 백엔드 핵심 모듈

  • 온라인 휴대폰 판매몰 백엔드에서는 단순 CRUD보다 운영 흐름이 중요합니다.
  • 현재 프로젝트 기준으로 핵심 모듈은 아래처럼 나눌 수 있습니다.
consult/
  상담 신청, 상담 목록, 상담 상세, 상태 변경, 메모, 중복 처리

product/
  상품, 옵션, 통신사, 가격/지원금, 노출 여부, 삭제/복구

admin/
  관리자 계정, 로그인, 권한, 역할, 비활성화

audit-log/
  관리자 작업 이력, 권한 변경, 상품 변경, 다운로드 기록

export/
  엑셀 ExportJob, 파일 생성, 다운로드 URL, 만료 처리

notification/
  알림톡/SMS Job, 발송 Worker, 발송 결과, 재시도

webhook/
  외부 이벤트 수신, signature 검증, 중복 처리, processor

analytics/
  유입 source, visitorId, 광고/SEO/전환 분석

common/
  error handling, requestId, logger, clock, idGenerator, config

➕ 3-1. 우선순위가 높은 모듈

1. consult
상담 신청과 관리자 처리의 중심

2. product
고객 화면과 판매 조건의 기준

3. admin/permission
관리자 기능의 안전장치

4. audit-log
운영 추적성과 책임 소재

5. export/notification
개인정보 파일과 외부 API 안정성
  • 모든 모듈을 한 번에 고도화하려고 하면 부담이 큽니다.
  • 상담, 상품, 권한, Audit Log, Export/Notification 순서가 현실적입니다.

✅ 4. 전체 아키텍처 기준

Controller
  ↓
Use Case
  ↓
Domain Service / Policy
  ↓
Repository
  ↓
Prisma
  ↓
PostgreSQL

Use Case / Worker
  ↓
Adapter
  ↓
External API

➕ 4-1. 계층별 역할

계층역할
ControllerHTTP 요청/응답, DTO, 현재 사용자 전달
Use Case하나의 업무 흐름 실행
Domain Service상태 전이, 중복 정책, 가격 정책 등 규칙
Policy권한과 조건 판단
RepositoryDB 조회/저장
Adapter외부 API, S3, 알림톡 등 연동
Worker오래 걸리거나 재시도가 필요한 작업 처리
Mapper내부 모델을 응답 DTO로 변환

➕ 4-2. 가장 중요한 기준

Controller는 얇게
Use Case는 업무 흐름
Domain Service는 규칙
Repository는 DB
Adapter는 외부 세계
  • 이 기준만 지켜도 코드가 훨씬 덜 섞입니다.
  • 구조를 복잡하게 만드는 것이 아니라, 섞이면 위험한 책임을 분리하는 것이 핵심입니다.

✅ 5. Controller 체크리스트

➕ 5-1. Controller가 해야 할 일

  • route 정의
  • DTO 받기
  • query/param/body 정리
  • 현재 관리자 정보 받기
  • requestId, ip, userAgent 전달
  • Use Case 호출
  • 응답 반환

➕ 5-2. Controller가 하면 안 좋은 일

  • Prisma query 직접 호출
  • 상태 전이 규칙 작성
  • 권한 세부 정책 하드코딩
  • 알림톡/SMS API 직접 호출
  • 엑셀 파일 직접 생성
  • Audit Log before/after 직접 복잡하게 조립
  • transaction 직접 관리
  • 응답에 DB 모델 그대로 반환

➕ 5-3. 좋은 Controller 예시

@Post(':id/status')
@RequirePermissions('CONSULT_UPDATE_STATUS')
async updateStatus(
  @Param('id', ParseIntPipe) consultId: number,
  @Body() body: UpdateConsultStatusDto,
  @CurrentAdmin() admin: CurrentAdminDto,
  @Req() req: Request,
) {
  return this.updateConsultStatusUseCase.execute({
    consultId,
    nextStatus: body.status,
    reason: body.reason,
    memo: body.memo,
    admin,
    requestId: req.headers['x-request-id']?.toString(),
    ipAddress: req.ip,
    userAgent: req.headers['user-agent'],
  });
}
  • Controller가 얇으면 테스트와 리팩토링이 쉬워집니다.
  • Controller에 로직이 많아지는 순간 구조가 무너지는 신호로 보면 됩니다.

✅ 6. Use Case 체크리스트

➕ 6-1. Use Case가 해야 할 일

  • 하나의 업무 흐름 담당
  • 권한 Policy 호출
  • Domain Service로 규칙 검증
  • Repository 호출
  • transaction boundary 관리
  • Audit Log 저장
  • 필요 시 Job row 생성
  • Mapper로 응답 변환

➕ 6-2. Use Case 이름 예시

CreateConsultUseCase
UpdateConsultStatusUseCase
RequestConsultExportUseCase
DownloadExportFileUseCase
UpdateProductPriceUseCase
SoftDeleteProductUseCase
ResendNotificationUseCase
ReceiveWebhookEventUseCase

➕ 6-3. 좋은 Use Case 기준

이름만 봐도 업무 목적이 보임
한 파일이 너무 크지 않음
transaction 범위가 명확함
외부 API 직접 호출이 없음
Repository와 Domain Service를 조합함
실패 시 어떤 에러가 나는지 명확함
  • Use Case는 백엔드의 실무 흐름을 표현합니다.
  • “상담 상태 변경”처럼 운영적으로 중요한 행동은 반드시 Use Case로 분리할 가치가 있습니다.

✅ 7. Domain Service / Policy 체크리스트

➕ 7-1. Domain Service 후보

ConsultStatusService:
상담 상태 전이 규칙

DuplicateConsultPolicy:
중복 상담 신청 기준

ConsultSnapshotFactory:
상담 당시 상품 조건 snapshot 생성

ProductVisibilityPolicy:
상품 노출 가능 여부 판단

ProductPricePolicy:
지원금/가격 변경 규칙

ExportPermissionPolicy:
엑셀 다운로드 권한 판단

ConsultPermissionPolicy:
상담 상태 변경 세부 권한 판단

➕ 7-2. 체크리스트

  • 비즈니스 규칙이 Controller에 흩어져 있지 않은가?
  • 같은 상태 전이 규칙이 여러 파일에 중복되어 있지 않은가?
  • 권한 판단과 상태 전이 판단이 분리되어 있는가?
  • DB 없이 테스트 가능한 규칙은 분리되어 있는가?
  • 정책 변경 시 수정 위치가 명확한가?
  • 예외 메시지가 운영자가 이해할 수 있는가?
  • 과도하게 많은 책임을 가진 Domain Service가 없는가?
  • Unit Test가 있는가?

➕ 7-3. 기준

규칙:
Domain Service

권한:
Policy

DB 조회/저장:
Repository

업무 흐름:
Use Case
  • 규칙이 한 곳에 있어야 정책 변경이 쉬워집니다.
  • 중복된 if문은 나중에 운영 버그로 이어질 가능성이 큽니다.

✅ 8. Repository 체크리스트

➕ 8-1. 핵심 Repository

ConsultRepository
ProductRepository
AdminUserRepository
AuditLogRepository
ExportJobRepository
NotificationJobRepository
WebhookEventRepository

➕ 8-2. 체크리스트

  • Prisma query가 Repository에 모여 있는가?
  • Repository가 비즈니스 규칙까지 판단하지 않는가?
  • transaction client를 선택적으로 받을 수 있는가?
  • Soft Delete 조건이 누락되지 않는가?
  • 고객용 조회와 관리자용 조회가 구분되는가?
  • 목록 조회와 상세 조회가 구분되는가?
  • 목록 조회는 select로 최소 필드만 가져오는가?
  • raw SQL은 Repository 내부에 모여 있고 이유가 명확한가?

➕ 8-3. Repository 메서드 이름 기준

좋음:
findAdminList
findDetailForAdmin
findActiveById
findDeletedById
updateStatusIfCurrent
createStatusHistory
createWithSnapshot
markJobDone
markJobFailed

나쁨:
get
find
update
save
handle
process
data
  • Repository 메서드 이름은 조회 목적과 조건을 드러내야 합니다.
  • 특히 findActiveById, findAdminList, findDetailForAdmin처럼 고객/관리자/삭제 조건을 이름에 담는 것이 좋습니다.

✅ 9. Transaction 체크리스트

➕ 9-1. Transaction이 필요한 작업

상담 상태 변경 + 상태 이력 + Audit Log
상담 신청 생성 + NotificationJob 생성
상품 가격 변경 + Audit Log
상품 삭제/복구 + Audit Log
ExportJob 생성 + Audit Log
관리자 권한 변경 + Audit Log
Webhook 이벤트 처리 + 내부 상태 반영

➕ 9-2. Transaction 안에 넣지 말 것

알림톡/SMS 실제 발송
외부 API 호출
S3 업로드
엑셀 파일 생성
대량 파일 처리
긴 반복문 작업
사용자 응답 대기

➕ 9-3. 체크리스트

  • transaction boundary가 Use Case에 있는가?
  • Controller가 transaction을 열지 않는가?
  • Repository 내부에 숨은 transaction이 없는가?
  • 같은 업무 흐름의 DB 작업이 같은 tx를 쓰는가?
  • 상태 변경과 이력/Audit Log가 같은 tx인가?
  • 외부 API 호출은 transaction 밖으로 분리되어 있는가?
  • transaction 시간이 길어질 작업이 없는가?
  • 실패 시 rollback 범위가 명확한가?
  • transaction은 데이터 정합성을 지키는 도구입니다.
  • 하지만 너무 오래 잡으면 성능과 장애 위험이 커지므로 짧고 명확하게 사용해야 합니다.

✅ 10. Queue/Worker 체크리스트

➕ 10-1. Worker로 분리할 작업

엑셀 Export
알림톡/SMS 발송
S3 파일 업로드/삭제
Webhook 후속 처리
EP 파일 생성
대량 유입 분석 집계
오래된 Export 파일 cleanup
외부 CRM/광고 API 동기화

➕ 10-2. Job 상태

PENDING
PROCESSING
DONE
FAILED
RETRYING
CANCELED
EXPIRED

➕ 10-3. 체크리스트

  • API 요청에서 오래 걸리는 작업을 직접 처리하지 않는가?
  • Job 테이블에 상태와 실패 사유가 저장되는가?
  • Worker가 같은 Job을 중복 처리하지 않도록 claim하는가?
  • retryCount와 maxRetry 기준이 있는가?
  • PROCESSING에 오래 멈춘 Job을 감지하는가?
  • Worker 로그에 jobId, jobType, durationMs가 남는가?
  • 실패한 Job을 관리자 화면에서 확인할 수 있는가?
  • 외부 API Secret/전화번호 원본이 로그에 남지 않는가?
  • Worker는 보이지 않는 곳에서 도는 운영 기능입니다.
  • 상태, 로그, 실패 사유, 재시도 기준이 없으면 나중에 반드시 답답해집니다.

✅ 11. Webhook / 외부 API 체크리스트

➕ 11-1. 외부 API 호출

  • 외부 API 호출이 Adapter로 분리되어 있는가?
  • timeout이 설정되어 있는가?
  • retryable/non-retryable 에러를 구분하는가?
  • rate limit 대응 기준이 있는가?
  • idempotency key를 사용할 수 있는가?
  • providerMessageId를 저장하는가?
  • 외부 API 원본 응답 전체를 저장하지 않는가?
  • Authorization header/API Key가 로그에 남지 않는가?

➕ 11-2. Webhook 수신

  • signature 검증이 있는가?
  • raw body 검증이 필요한지 확인했는가?
  • timestamp 검증 또는 replay 방지가 있는가?
  • provider + providerEventId unique가 있는가?
  • 중복 Webhook을 비즈니스 처리하지 않는가?
  • 중복 수신 시 보통 200으로 응답하는가?
  • Controller는 빠르게 응답하는가?
  • 실제 처리는 Processor/Worker로 분리되어 있는가?

➕ 11-3. 기준

외부 API:
나가는 요청

Webhook:
들어오는 이벤트

공통:
검증, timeout, 중복 처리, 재시도, 로그, 보안
  • 외부 연동은 성공 케이스보다 실패 케이스가 더 중요합니다.
  • 운영에서는 “한 번 실패했을 때 어떻게 복구되는가”가 품질을 결정합니다.

✅ 12. Permission 체크리스트

➕ 12-1. 권한 코드 후보

CONSULT_READ
CONSULT_DETAIL_READ
CONSULT_UPDATE_STATUS
CONSULT_MEMO_WRITE
CONSULT_EXPORT

PRODUCT_READ
PRODUCT_CREATE
PRODUCT_UPDATE
PRODUCT_PRICE_UPDATE
PRODUCT_DELETE
PRODUCT_RESTORE

BANNER_READ
BANNER_UPDATE

NOTIFICATION_READ
NOTIFICATION_RESEND

EXPORT_JOB_READ
EXPORT_JOB_DOWNLOAD

AUDIT_LOG_READ
AUDIT_LOG_DETAIL_READ

ADMIN_USER_READ
ADMIN_USER_CREATE
ADMIN_USER_UPDATE
ADMIN_USER_DISABLE
ADMIN_PERMISSION_MANAGE

➕ 12-2. 체크리스트

  • 인증과 인가를 구분했는가?
  • Role 이름보다 PermissionCode 기준으로 판단하는가?
  • RequirePermissions decorator가 있는가?
  • PermissionGuard가 실제 route에 적용되어 있는가?
  • 권한 없는 요청은 403을 반환하는가?
  • 조회 권한과 엑셀 다운로드 권한이 분리되어 있는가?
  • 관리자 계정 관리 권한이 별도로 있는가?
  • 권한 변경은 Audit Log에 남는가?

➕ 12-3. 프론트 권한 기준

프론트:
메뉴/버튼 숨김

백엔드:
최종 권한 검증

주의:
프론트 숨김은 보안이 아님
  • 권한은 프론트에서 숨기는 것으로 끝나면 안 됩니다.
  • 백엔드 API에서 반드시 검증해야 합니다.

✅ 13. Audit Log 체크리스트

➕ 13-1. Audit 대상 작업

상담 상태 변경
상담 메모 수정
상품 가격/지원금 변경
상품 삭제/복구
엑셀 Export 요청
Export 파일 다운로드
알림톡 수동 재발송
Webhook 수동 재처리
관리자 권한 변경
관리자 계정 비활성화

➕ 13-2. AuditLog 필드

actorType
actorId
action
targetType
targetId
beforeValue
afterValue
metadata
requestId
ipAddress
userAgent
createdAt

➕ 13-3. 체크리스트

  • 중요한 변경 작업과 Audit Log가 같은 transaction인가?
  • action 이름이 명확한가?
  • actorType/actorId가 저장되는가?
  • targetType/targetId가 저장되는가?
  • beforeValue/afterValue는 변경된 필드만 저장하는가?
  • requestId가 저장되는가?
  • Audit Log 조회 권한이 제한되어 있는가?
  • Audit Log에 개인정보/Secret이 들어가지 않는가?

➕ 13-4. 민감정보 금지 목록

전화번호 원본
phoneNormalized
passwordHash
accessToken
refreshToken
Authorization header
cookie
DATABASE_URL
API Key
Secret
상담 메모 전체
  • Audit Log는 운영 추적성을 높이지만, 잘못 만들면 민감정보 저장소가 됩니다.
  • 전체 객체를 그대로 넣는 방식은 피해야 합니다.

✅ 14. Error Handling 체크리스트

➕ 14-1. 표준 에러 응답

{
  "success": false,
  "error": {
    "code": "CONSULT_STATUS_CONFLICT",
    "message": "이미 다른 관리자가 상담 상태를 변경했습니다.",
    "details": null
  },
  "requestId": "req_123",
  "timestamp": "2026-09-02T09:00:00.000Z",
  "path": "/admin/consults/10/status"
}

➕ 14-2. 체크리스트

  • 모든 에러 응답 구조가 통일되어 있는가?
  • error.code가 있는가?
  • requestId가 포함되어 있는가?
  • GlobalExceptionFilter가 적용되어 있는가?
  • Validation Error details가 프론트에서 쓰기 좋은가?
  • Prisma 에러 원문이 응답에 노출되지 않는가?
  • unknown error가 안전한 500으로 변환되는가?
  • stack trace가 응답에 노출되지 않는가?

➕ 14-3. 도메인 에러 코드 후보

CONSULT_NOT_FOUND
CONSULT_DUPLICATED
CONSULT_STATUS_TRANSITION_INVALID
CONSULT_STATUS_CONFLICT
PRODUCT_NOT_FOUND
PRODUCT_INACTIVE
EXPORT_FILE_EXPIRED
EXPORT_PERMISSION_DENIED
NOTIFICATION_PROVIDER_TIMEOUT
WEBHOOK_SIGNATURE_INVALID
  • 에러 응답은 프론트와 백엔드의 계약입니다.
  • 한 번 정하면 쉽게 바꾸기 어려우므로 초기에 기준을 잘 잡아야 합니다.

✅ 15. 테스트 체크리스트

➕ 15-1. 우선 테스트할 기능

ConsultStatusService
ConsultPermissionPolicy
DuplicateConsultPolicy
UpdateConsultStatusUseCase
RequestConsultExportUseCase
NotificationWorker
ExportWorker
PermissionGuard
GlobalExceptionFilter
Webhook signature 검증

➕ 15-2. 체크리스트

  • Domain Service는 Unit Test가 있는가?
  • Use Case는 Integration Test로 DB 정합성을 확인하는가?
  • Guard/Controller는 E2E Test로 검증하는가?
  • 외부 API/S3는 Mock 처리하는가?
  • 테스트 DB와 운영 DB가 완전히 분리되어 있는가?
  • Seed Factory가 있는가?
  • new Date()와 randomUUID()를 Mock할 수 있는가?
  • 표준 에러 응답 테스트가 있는가?

➕ 15-3. 테스트 DB 안전 기준

NODE_ENV=test 확인
DATABASE_URL에 prod 포함 시 실행 중단
운영 Secret 사용 금지
테스트 전후 데이터 초기화
외부 API 실제 호출 금지
  • 테스트는 리팩토링의 보험입니다.
  • 특히 AI/Codex에게 코드를 맡길수록 테스트의 가치는 더 커집니다.

✅ 16. 보안 체크리스트

➕ 16-1. 관리자 보안

  • 비활성 관리자는 로그인할 수 없는가?
  • 권한 없는 API 요청은 403으로 막히는가?
  • 마지막 SUPER_ADMIN 비활성화를 막는가?
  • 권한 변경 시 token/session 무효화 기준이 있는가?
  • 관리자 계정 변경은 Audit Log에 남는가?
  • 로그인 실패 반복 대응 기준이 있는가?
  • 관리자 페이지 접근 경로가 보호되어 있는가?
  • /me 응답에 민감정보가 없는가?

➕ 16-2. 개인정보 보안

  • 전화번호 원본을 불필요하게 응답하지 않는가?
  • 목록에서는 phoneMasked만 내려주는가?
  • Export 파일 다운로드 권한이 분리되어 있는가?
  • Export 파일 만료 정책이 있는가?
  • 상담 메모 전체를 로그에 남기지 않는가?
  • Webhook payload 저장 기준이 있는가?
  • 운영 dump를 AI/공용 저장소에 올리지 않는가?
  • 로그에 개인정보가 남지 않는가?

➕ 16-3. Secret 보안

코드에 Secret 하드코딩 금지
Git 커밋 금지
SSM/Secrets Manager 사용
운영/개발 Secret 분리
Authorization header 로그 금지
DATABASE_URL 로그 금지
외부 API Key 로그 금지
주기적 rotation 고려
  • 백엔드 아키텍처는 보안과 분리할 수 없습니다.
  • 특히 관리자 기능, Export 파일, 외부 API Secret은 사고가 나면 피해가 큽니다.

✅ 17. 운영 모니터링 체크리스트

➕ 17-1. API 로그

  • requestId가 모든 요청에 있는가?
  • method/path/statusCode/durationMs가 기록되는가?
  • adminId 또는 actorId가 필요한 경우 기록되는가?
  • 5xx 에러가 별도로 확인되는가?
  • 403/409 같은 운영 에러를 추적할 수 있는가?
  • 느린 API를 식별할 수 있는가?
  • 민감정보가 로그에 남지 않는가?
  • 배포 전후 에러 증가를 확인할 수 있는가?

➕ 17-2. Worker 모니터링

  • PENDING Job이 과도하게 쌓이지 않는가?
  • PROCESSING Job이 오래 멈춰 있지 않은가?
  • FAILED Job이 급증하지 않는가?
  • retryCount가 반복적으로 증가하지 않는가?
  • Notification 발송 실패율을 확인할 수 있는가?
  • Export 생성 실패율을 확인할 수 있는가?
  • Worker 프로세스가 죽었는지 확인할 수 있는가?
  • 외부 API 장애와 내부 장애를 구분할 수 있는가?

➕ 17-3. DB 모니터링

  • 느린 쿼리를 확인할 수 있는가?
  • 관리자 목록 API 응답 시간이 증가하지 않는가?
  • connection 오류가 없는가?
  • migration 이후 에러가 증가하지 않는가?
  • ExportJob/NotificationJob 테이블이 과도하게 커지지 않는가?
  • AuditLog 보존 정책이 있는가?
  • 백업 상태를 확인했는가?
  • 복구 테스트 기준이 있는가?
  • 운영 모니터링은 문제가 없을 때 준비해야 합니다.
  • 장애가 난 뒤에 로그가 없으면 추측만 하게 됩니다.

✅ 18. 배포 전 백엔드 체크리스트

➕ 18-1. 코드 체크

  • typecheck 통과
  • lint 통과
  • unit test 통과
  • integration test 통과
  • 주요 e2e test 통과
  • 환경변수 누락 없음
  • Secret 하드코딩 없음
  • console.log에 개인정보 없음

➕ 18-2. DB 체크

  • Prisma migration 파일 확인
  • destructive change 여부 확인
  • staging/test DB에서 migration 검증
  • 운영 DB backup/snapshot 필요 여부 확인
  • nullable/default/unique constraint 안전성 확인
  • index 추가 시 lock/시간 고려
  • rollback 또는 forward fix 계획
  • 배포 후 smoke test 준비

➕ 18-3. 기능 체크

  • 상담 신청 정상
  • 상담 목록 조회 정상
  • 상담 상태 변경 정상
  • 상태 이력 생성 정상
  • Audit Log 생성 정상
  • 권한 없는 요청 403 정상
  • ExportJob 생성 정상
  • NotificationJob 생성/처리 정상
  • 배포 전 체크리스트는 귀찮아도 사고를 줄입니다.
  • 특히 DB migration이 포함된 배포는 평소보다 더 보수적으로 해야 합니다.

✅ 19. 배포 후 Smoke Test

  • Smoke Test는 배포 후 핵심 기능이 살아 있는지 빠르게 확인하는 테스트입니다.
  • 모든 기능을 다 확인하는 것이 아니라, 서비스가 정상 운영 가능한지 보는 최소 점검입니다.
배포 완료
  ↓
health check
  ↓
로그인 확인
  ↓
상담 신청 확인
  ↓
관리자 목록 확인
  ↓
상태 변경 확인
  ↓
Worker/Job 확인

➕ 19-1. Smoke Test 항목

  • 백엔드 health check 정상
  • 관리자 로그인 정상
  • /me 권한 응답 정상
  • 상담 목록 조회 정상
  • 상담 상세 조회 정상
  • 상담 상태 변경 정상
  • 상태 변경 이력 생성 정상
  • Audit Log 생성 정상
  • 상담 신청 저장 정상
  • NotificationJob 생성 정상
  • ExportJob 요청 정상
  • 에러 로그 급증 없음

➕ 19-2. 배포 후 확인할 로그

5xx error 증가
Prisma error 증가
권한 403 비정상 증가
Webhook signature 오류 증가
NotificationJob FAILED 증가
ExportJob FAILED 증가
slow query 증가
DB connection error
  • 배포는 “끝”이 아니라 “검증 시작”입니다.
  • 배포 직후 10~30분 로그를 보는 습관이 중요합니다.

✅ 20. 장애 대응 Runbook 통합

  • 아키텍처가 아무리 좋아도 장애는 발생할 수 있습니다.
  • 중요한 것은 장애가 났을 때 어디를 먼저 보고, 어떤 순서로 판단할지 정해두는 것입니다.
# Runbook: 백엔드 장애 대응

## 1. 상황 확인
- 발생 시각:
- 영향 범위:
- 고객 화면/관리자 화면:
- 특정 API:
- 최근 배포 여부:
- 에러 코드:
- requestId:
- jobId:
- 관련 관리자/상담/상품 ID:

## 2. 즉시 확인
- 5xx 로그 증가 여부
- DB connection 오류
- Prisma error
- Worker 상태
- FAILED Job 증가
- 외부 API 장애 여부
- 최근 migration 여부
- 권한/인증 오류 증가 여부

## 3. 분류
- 코드 버그
- DB/migration 문제
- 외부 API 장애
- 권한 설정 문제
- Worker 중단
- 데이터 정합성 문제
- 인프라/배포 문제

## 4. 조치
- 영향 API 임시 차단 여부
- Worker 일시 중지 여부
- rollback 또는 forward fix
- 실패 Job 재처리 여부
- 잘못된 데이터 수동 복구 여부
- 관리자/고객 안내 필요 여부

## 5. 복구 후
- Smoke Test
- 에러 로그 감소 확인
- Audit Log/Job 상태 확인
- 장애 원인 문서화
- 재발 방지 TODO 등록
  • Runbook은 사고 때 머리로 생각할 일을 줄여줍니다.
  • 1인 개발자일수록 문서화된 대응 순서가 필요합니다.

✅ 21. AI/Codex 작업 규칙 통합

  • AI/Codex를 실무에 쓰려면 코드 구조와 작업 규칙이 중요합니다.
  • 규칙이 없으면 AI가 빠르게 코드를 바꾸는 만큼 빠르게 구조를 망칠 수 있습니다.

➕ 21-1. 공통 작업 규칙

Controller에 비즈니스 로직 넣지 않기
Use Case 단위로 업무 흐름 작성
Domain Service/Policy에 규칙 분리
Repository에 Prisma query 모으기
transaction은 Use Case에서 관리
외부 API는 Adapter/Worker로 분리
표준 에러 응답 유지
민감정보 응답/로그 금지
테스트 또는 QA 체크리스트 포함

➕ 21-2. DB/운영 작업 금지 기준

운영 DB 접속 정보 제공 금지
운영 개인정보 제공 금지
운영 migration 자동 승인 금지
destructive migration 자동 적용 금지
운영 dump AI 업로드 금지
Secret/API Key 전달 금지
where 없는 update/delete 금지

➕ 21-3. Codex에게 작업 맡길 때 포함할 것

작업 목적
수정 범위
건드리면 안 되는 파일/기능
기존 API 응답 유지 여부
권한 기준
transaction 기준
Audit Log 기준
민감정보 금지 기준
테스트/QA 요구사항
변경 파일 목록 요청
  • AI는 명확한 구조와 규칙이 있을 때 더 강력해집니다.
  • “알아서 잘 해줘”는 실무 백엔드 작업에서 위험합니다.

✅ 22. 백엔드 아키텍처 문서 구조 추천

  • 아키텍처는 코드만으로 유지되지 않습니다.
  • 최소한의 문서가 있어야 본인도 나중에 잊지 않고, AI/Codex에게도 기준을 줄 수 있습니다.
docs/
  backend/
    README.md
    architecture.md
    module-structure.md
    use-case-guideline.md
    repository-guideline.md
    transaction-guideline.md
    permission-policy.md
    audit-log.md
    error-handling.md
    queue-worker.md
    webhook-external-api.md
    testing.md
    deployment-checklist.md
    incident-runbook.md

➕ 22-1. architecture.md

전체 계층 구조
Controller/Use Case/Domain/Repository/Adapter 역할
모듈별 책임
금지 패턴
예시 코드 링크

➕ 22-2. transaction-guideline.md

transaction boundary 기준
transaction 안에 넣을 작업
transaction 밖으로 뺄 작업
상태 변경 예시
주의사항

➕ 22-3. error-handling.md

표준 응답 구조
에러 코드 목록
Exception Filter 기준
Validation Error 처리
Prisma Error 변환
프론트 공통 처리 기준

➕ 22-4. testing.md

Unit/Integration/E2E 기준
Test DB 설정
Mock 대상
Seed Factory 사용법
CI 테스트 실행법
우선 테스트할 기능
  • 문서는 길게 쓰는 것보다 실제 작업 시 참고할 수 있어야 합니다.
  • Codex 작업 지시에도 그대로 붙일 수 있는 형태가 좋습니다.

✅ 23. 현재 프로젝트 4주 적용 로드맵

➕ 23-1. 1주차: 상담 상태 변경 구조화

목표:
상담 상태 변경 흐름 안정화

작업:
- UpdateConsultStatusUseCase 분리
- ConsultStatusService 작성
- ConsultPermissionPolicy 작성
- ConsultRepository 정리
- 상태 변경 + 이력 + Audit Log transaction 적용
- 409 동시성 충돌 처리

완료 기준:

상태 변경 로직이 Controller/Service에서 분리됨
상태 이력과 Audit Log가 누락되지 않음
권한/상태 전이/동시성 기준이 명확함

➕ 23-2. 2주차: 권한과 에러 응답 표준화

목표:
관리자 API 접근 제어와 에러 응답 통일

작업:
- PermissionCode 정의
- RequirePermissions decorator
- PermissionGuard 적용
- /me 권한 응답 정리
- AppException 작성
- GlobalExceptionFilter 적용
- 주요 도메인 에러 코드 정리

완료 기준:

권한 없는 요청은 403 처리
엑셀 다운로드/상품 수정/관리자 관리 권한 분리
모든 에러 응답에 error.code와 requestId 포함

➕ 23-3. 3주차: ExportJob/NotificationJob 분리

목표:
오래 걸리는 작업과 외부 API 발송 분리

작업:
- ExportJob 테이블/Repository
- RequestConsultExportUseCase
- ExportWorker 초안
- NotificationJob 테이블/Repository
- NotificationWorker 초안
- AlimtalkAdapter 분리
- retryCount/errorCode 저장

완료 기준:

엑셀 생성이 API 요청과 분리됨
상담 저장과 알림톡 발송이 분리됨
Job 상태와 실패 사유를 관리자/로그에서 확인 가능

➕ 23-4. 4주차: 테스트와 운영 문서화

목표:
리팩토링 안전장치와 운영 대응 기준 확보

작업:
- ConsultStatusService unit test
- UpdateConsultStatusUseCase integration test
- PermissionGuard e2e test
- GlobalExceptionFilter e2e test
- Worker Mock test
- backend architecture docs 작성
- deployment checklist / incident runbook 작성

완료 기준:

핵심 흐름이 테스트로 보호됨
AI/Codex 작업 전후 검증 가능
배포/장애 대응 기준 문서화
  • 이 로드맵은 “완벽한 아키텍처”가 아니라 “사고 가능성이 큰 부분부터 줄이는 순서”입니다.
  • 지금 프로젝트에는 이 순서가 가장 현실적입니다.

✅ 24. 백엔드 작업을 성과로 표현하는 방법

  • 백엔드 아키텍처 개선은 겉으로 고객에게 바로 보이지 않을 수 있습니다.
  • 하지만 경력기술서, 포트폴리오, 연봉협상에서는 운영 안정성, 확장성, 추적성, 자동화 품질로 강하게 표현할 수 있습니다.

➕ 24-1. 약한 표현

Service 리팩토링
Repository 추가
권한 Guard 추가
에러 처리 수정
테스트 작성
Worker 추가

➕ 24-2. 강한 표현

상담 상태 변경 로직을 Use Case 단위로 분리하고, 상태 변경·이력 저장·Audit Log 생성을 하나의 transaction으로 묶어 운영 데이터 정합성을 개선했습니다.

관리자 API에 Permission 기반 접근 제어를 도입해 상담 조회, 상태 변경, 엑셀 다운로드, 상품 수정, 관리자 계정 관리 권한을 분리했습니다.

엑셀 Export와 알림톡 발송을 Job/Worker 구조로 분리해 API 응답 지연과 외부 API 장애 영향을 줄이고, 실패 상태와 재시도 기준을 관리할 수 있게 했습니다.

Global Exception Filter와 표준 에러 응답 구조를 적용해 프론트 공통 에러 처리와 운영 로그 추적성을 개선했습니다.

Audit Log와 requestId 기반 추적 구조를 정리해 상품/지원금 변경, 상담 상태 변경, 엑셀 다운로드, 권한 변경 이력을 추적할 수 있게 했습니다.

Domain Service, Repository, Adapter, Worker 계층을 분리해 AI/Codex 기반 리팩토링 시 수정 범위를 통제하고 테스트 가능한 구조를 마련했습니다.

상태 전이, 권한 Policy, 상태 변경 Use Case, Worker 실패 처리에 대한 테스트를 추가해 핵심 운영 흐름의 회귀 버그를 줄였습니다.
  • 성과 표현의 핵심은 “코드를 나눴다”가 아닙니다.
  • “운영 리스크가 어떻게 줄었는가”, “확장과 유지보수가 어떻게 쉬워졌는가”로 말해야 합니다.

✅ 25. 과한 아키텍처를 피하는 기준

  • 구조화는 필요하지만, 1인 개발자 프로젝트에서 과한 아키텍처는 오히려 독이 될 수 있습니다.
  • 지금 필요한 것은 대규모 엔터프라이즈 구조가 아니라, 운영에 필요한 최소한의 책임 분리입니다.

➕ 25-1. 과한 구조의 신호

간단한 CRUD 하나에 파일 8개 수정
테스트도 없는데 interface만 많음
실제 외부 시스템이 없는데 Event Sourcing 도입
트래픽도 적은데 Redis/BullMQ부터 도입
관리자 2명인데 복잡한 RBAC UI부터 구현
변경 속도보다 구조 유지 비용이 더 큼

➕ 25-2. 현실적인 기준

단순 조회:
Controller + Service/Repository로 충분

중요 업무 흐름:
Use Case 분리

중복 규칙:
Domain Service/Policy 분리

외부 API:
Adapter 분리

오래 걸리는 작업:
Job/Worker 분리

위험 작업:
Permission + Audit Log + Test

➕ 25-3. 현재 기준에서 하지 않아도 되는 것

전면 Clean Architecture 강제
모든 Repository에 interface 도입
CQRS/Event Sourcing 전면 도입
Microservice 분리
Kubernetes 기반 Worker 운영
복잡한 권한 관리 UI 선구현
  • 아키텍처는 문제를 줄이기 위한 수단입니다.
  • 구조 자체가 개발 속도를 잡아먹기 시작하면 방향을 다시 봐야 합니다.

✅ 26. AI에게 백엔드 아키텍처 전체 점검을 맡길 때 좋은 질문법

NestJS + Prisma + PostgreSQL 기반 온라인 휴대폰 판매몰 백엔드 아키텍처를 전체 점검하고 싶어.

서비스 상황:
1. 고객 상담 신청, 관리자 상담 목록/상세, 상담 상태 변경, 상담 메모, 상품/옵션 관리, 엑셀 Export, 알림톡 발송, Webhook 수신, 관리자 권한, Audit Log 기능이 있음
2. 상담 상태 변경은 상태 전이 검증, 권한 확인, consults.status 업데이트, consult_status_histories 생성, audit_logs 생성을 하나의 transaction으로 묶고 싶음
3. 엑셀 Export와 알림톡 발송은 API 요청에서 직접 처리하지 않고 ExportJob/NotificationJob + Worker로 분리하고 싶음
4. 외부 API 호출은 Adapter로 분리하고 timeout, retry/backoff, idempotency를 적용하고 싶음
5. Webhook은 signature 검증, providerEventId unique, 빠른 응답, Processor/Worker 처리가 필요함
6. 관리자 권한은 Role 이름보다 PermissionCode 기준으로 판단하고, 엑셀 다운로드/상품 수정/관리자 계정 관리는 별도 권한으로 분리하고 싶음
7. Audit Log는 상담 상태 변경, 상품 가격/지원금 변경, 엑셀 다운로드, 알림톡 재발송, 관리자 권한 변경을 기록하고 싶음
8. 에러 응답은 success=false, error.code, error.message, requestId, timestamp, path 구조로 표준화하고 싶음
9. 테스트는 Unit/Integration/E2E로 나누고 외부 API/S3/시간/랜덤값은 Mock하고 싶음
10. 현재는 1인 개발자 프로젝트라 과한 구조보다는 운영 리스크가 큰 부분부터 점진적으로 개선하고 싶음

요청:
- 현재 구조에서 위험한 부분
- Controller/Use Case/Domain Service/Repository/Adapter/Worker 역할 점검
- transaction boundary 점검
- 권한/Permission Policy 점검
- Audit Log/운영 추적성 점검
- Error Handling/표준 응답 점검
- Queue/Worker/Webhook/외부 API 점검
- 테스트 전략 점검
- 보안/개인정보/Secret 관리 체크리스트
- 4주 개선 로드맵
- 과한 아키텍처를 피하는 기준
- 포트폴리오/경력기술서에 쓸 성과 표현
을 실무 기준으로 정리해줘.

➕ 26-1. AI 답변 검증 기준

현재 프로젝트의 상담/상품/관리자 흐름을 중심으로 보는가?
무조건 대규모 아키텍처를 강요하지 않는가?
transaction과 Audit Log 정합성을 강조하는가?
외부 API를 transaction 밖으로 분리하는가?
권한을 프론트 숨김이 아닌 백엔드 검증으로 보는가?
엑셀 Export와 개인정보 파일 보안을 중요하게 보는가?
표준 에러 응답과 requestId를 제안하는가?
테스트 DB와 운영 DB 분리를 강조하는가?
AI/Codex 작업 규칙까지 포함하는가?

✅ 27. 최종 백엔드 아키텍처 체크리스트

➕ 27-1. 설계

  • 핵심 도메인 모듈이 분리되어 있는가?
  • Controller/Use Case/Domain/Repository 역할이 섞이지 않는가?
  • 복잡한 업무 흐름은 Use Case로 분리되어 있는가?
  • 중복되는 비즈니스 규칙은 Domain Service/Policy로 분리되어 있는가?
  • Prisma query는 Repository에 모여 있는가?
  • 외부 API는 Adapter로 분리되어 있는가?
  • 오래 걸리는 작업은 Worker로 분리되어 있는가?
  • 응답 DTO/Mapper로 민감정보 노출을 막는가?

➕ 27-2. 운영

  • 상태 변경과 이력/Audit Log가 transaction으로 묶이는가?
  • Job 상태와 실패 사유를 확인할 수 있는가?
  • requestId로 API/Audit/Worker 로그를 연결할 수 있는가?
  • 권한 없는 요청이 백엔드에서 차단되는가?
  • Export 파일 다운로드 권한과 만료 정책이 있는가?
  • Webhook 중복/위조 요청을 막는가?
  • 표준 에러 응답이 유지되는가?
  • 배포 전후 체크리스트가 있는가?

➕ 27-3. 보안

  • 전화번호 원본이 불필요하게 응답되지 않는가?
  • 로그에 개인정보/Secret이 남지 않는가?
  • 운영 DB dump를 안전하게 관리하는가?
  • API Key와 DATABASE_URL이 코드에 하드코딩되어 있지 않은가?
  • 관리자 권한 변경이 Audit Log에 남는가?
  • 마지막 SUPER_ADMIN 비활성화를 막는가?
  • Webhook signature secret이 안전하게 관리되는가?
  • 테스트/AI 작업에 운영 데이터가 노출되지 않는가?

➕ 27-4. 테스트/자동화

  • 핵심 Domain Service에 Unit Test가 있는가?
  • 상태 변경 Use Case에 Integration Test가 있는가?
  • 권한/에러 응답에 E2E Test가 있는가?
  • 외부 API/S3는 Mock 가능한가?
  • CI에서 typecheck/lint/test가 실행되는가?
  • AI/Codex 작업 후 테스트로 회귀 버그를 확인하는가?
  • Worker 실패/재시도 테스트가 있는가?
  • 문서와 Runbook이 최신 상태인가?

📌 요약

  • 백엔드 아키텍처 고도화의 핵심은 패턴을 많이 쓰는 것이 아니라, 운영 리스크가 큰 기능의 책임을 분리하고 추적 가능하게 만드는 것입니다.
  • Controller는 얇게 유지하고, 복잡한 업무 흐름은 Use Case로 분리해야 합니다.
  • 상태 전이, 중복 신청, 권한 판단 같은 규칙은 Domain Service/Policy로 분리하면 테스트와 유지보수가 쉬워집니다.
  • Prisma query는 Repository에 모아 고객용/관리자용 조회, Soft Delete 조건, select 기준, transaction client 사용을 일관되게 관리하는 것이 좋습니다.
  • 상담 상태 변경, 상품 가격 변경, ExportJob 생성, 권한 변경처럼 중요한 DB 변경은 Audit Log와 함께 transaction으로 묶어야 합니다.
  • 알림톡/SMS 발송, 엑셀 생성, S3 업로드, Webhook 후속 처리처럼 오래 걸리거나 실패 가능성이 큰 작업은 Job/Worker로 분리해야 합니다.
  • 외부 API는 Adapter로 감싸고 timeout, retry/backoff, idempotency, providerMessageId, safe error message 기준을 가져야 합니다.
  • Webhook은 signature 검증, providerEventId unique, 중복 처리, 빠른 응답, Processor/Worker 분리가 핵심입니다.
  • 관리자 권한은 Role 이름보다 PermissionCode 기준으로 판단하고, 엑셀 다운로드/상품 수정/관리자 계정 관리/Audit Log 조회 같은 위험 기능은 별도 권한으로 분리해야 합니다.
  • Audit Log에는 누가, 언제, 어떤 작업을, 어떤 대상에 했는지 남기되 전화번호 원본, token, Secret, passwordHash, 상담 메모 전체는 저장하지 않아야 합니다.
  • Error Handling은 error.code, message, details, requestId가 포함된 표준 구조로 통일해야 프론트 처리와 운영 추적이 쉬워집니다.
  • 테스트는 Unit/Integration/E2E로 나누고, 상담 상태 변경·권한·ExportJob·NotificationJob·표준 에러 응답처럼 사고 가능성이 큰 흐름부터 우선 적용하는 것이 현실적입니다.
  • 1인 개발자 프로젝트에서는 전면 Clean Architecture나 복잡한 CQRS보다, 상담/상품/관리자/권한/로그/Worker 중심으로 점진적 구조화를 하는 것이 가장 효율적입니다.

0개의 댓글