TIL - 20260715

juni·2026년 7월 14일

TIL

목록 보기
404/468

0715 백엔드 실무 심화 (22/N): 코드 품질, 리팩토링과 기술부채 관리


✅ 1. 코드 품질이란 무엇인가?

  • 코드 품질(Code Quality)은 코드가 단순히 “동작하는지”를 넘어서, 읽기 쉽고, 수정하기 쉽고, 장애를 만들 가능성이 낮은 상태를 의미합니다.
  • 백엔드 실무에서는 기능이 빨리 만들어지는 것도 중요하지만, 시간이 지나도 유지보수할 수 있는 구조가 더 중요해집니다.
  • 특히 1인 개발 환경에서는 내가 짠 코드를 미래의 내가 다시 봐야 하기 때문에 코드 품질이 곧 운영 속도와 직결됩니다.
낮은 코드 품질:
기능은 동작함
하지만 수정할 때마다 다른 기능이 깨짐

높은 코드 품질:
기능이 동작함
수정 위치가 명확함
테스트와 문서로 영향 범위를 확인 가능

➕ 1-1. 코드 품질이 중요한 이유

  • 기능 추가 속도가 유지됩니다.
  • 버그 발생 가능성이 줄어듭니다.
  • 장애 대응 시간이 짧아집니다.
  • 리팩토링과 구조 개선이 쉬워집니다.
  • AI에게 작업을 맡길 때 결과물이 안정적입니다.
  • 인수인계와 경력 정리에 도움이 됩니다.

✅ 2. 동작하는 코드와 좋은 코드의 차이

  • 실무에서는 “일단 되게 만들기”가 필요한 순간이 있습니다.
  • 하지만 계속 그렇게만 쌓이면 유지보수가 어려운 코드가 됩니다.
구분동작하는 코드좋은 코드
목표지금 기능이 됨앞으로도 안전하게 수정 가능
구조한 파일에 몰림역할별로 분리
에러 처리상황마다 다름공통 규칙 있음
이름대충 알아볼 정도의도가 명확함
테스트수동 확인 위주핵심 로직 자동 테스트
변경 영향예측 어려움영향 범위가 보임

➕ 2-1. 나쁜 코드 예시

async updateConsult(id: number, dto: any) {
  const consult = await this.prisma.consult.findUnique({ where: { id } });

  if (!consult) {
    throw new Error('없음');
  }

  if (dto.status) {
    await this.prisma.consult.update({
      where: { id },
      data: {
        status: dto.status,
        memo: dto.memo,
        completedAt: dto.status === 'DONE' ? new Date() : null,
      },
    });

    if (dto.status === 'DONE') {
      await this.sendAlimtalk(consult.phone);
    }
  }

  return { ok: true };
}

문제점:

dto 타입이 any
상태 변경 규칙이 없음
이력 저장이 없음
트랜잭션이 없음
알림톡 중복 발송 가능
에러 메시지가 불명확
Service 하나에 책임이 너무 많음

✅ 3. 리팩토링이란 무엇인가?

  • 리팩토링(Refactoring)은 기능 동작은 유지하면서 코드 구조를 개선하는 작업입니다.
  • 새 기능을 만드는 것이 아니라, 기존 코드를 더 읽기 쉽고 수정하기 쉽게 정리하는 작업입니다.
기능 결과:
변하지 않음

코드 구조:
더 명확해짐
더 안전해짐
더 수정하기 쉬워짐

➕ 3-1. 리팩토링이 필요한 신호

같은 코드가 여러 곳에 반복됨
Service 메서드가 너무 김
if/else가 계속 중첩됨
한 함수가 여러 책임을 가짐
변경할 때마다 다른 기능이 깨짐
에러 처리 방식이 기능마다 다름
DTO와 DB 모델이 뒤섞임
파일 이름만 보고 역할을 알기 어려움
  • 리팩토링은 코드가 완전히 망가진 뒤 하는 것이 아닙니다.
  • 기능 추가가 점점 느려질 때, 작은 단위로 계속 해야 합니다.

✅ 4. 기술부채란 무엇인가?

  • 기술부채(Technical Debt)는 빠른 개발을 위해 임시로 선택한 코드나 구조가 나중에 유지보수 비용으로 돌아오는 것을 의미합니다.
  • 모든 기술부채가 나쁜 것은 아닙니다.
  • 중요한 것은 부채를 인식하고, 기록하고, 갚을 시점을 정하는 것입니다.

➕ 4-1. 기술부채 예시

중복 코드가 많음
관리자 권한 로직이 Controller마다 흩어져 있음
상태값이 문자열로 직접 비교됨
엑셀 다운로드가 동기 API로 처리됨
환경변수 검증이 없음
테스트 코드가 없음
API 응답 구조가 기능마다 다름
로그에 필요한 정보가 부족함

➕ 4-2. 괜찮은 기술부채

MVP 단계에서 빠르게 수동 배포
초기에는 단순 PM2 배포
데이터가 적어서 실시간 집계 사용
관리자 기능 초기 버전은 수동 QA 중심

➕ 4-3. 위험한 기술부채

권한 검사 누락
운영 DB 직접 수정 습관
중복 신청 방지 없음
상태 변경 이력 없음
개인정보 로그 노출
DB migration 롤백 계획 없음
외부 API 실패 처리 없음
  • 속도를 위해 남긴 부채라도 보안, 데이터 정합성, 결제, 개인정보, 권한 관련 부채는 빨리 갚아야 합니다.

✅ 5. 좋은 백엔드 구조의 기준

  • 백엔드는 역할별로 책임이 나뉘어 있어야 합니다.
  • NestJS 기준으로 Controller, Service, Repository, DTO, Entity/Model, Guard, Interceptor, Filter 역할을 분리하면 유지보수가 쉬워집니다.

➕ 5-1. 역할 분리 기준

계층역할
Controller요청/응답, 라우팅
DTO입력값 구조와 검증
Service비즈니스 로직
RepositoryDB 접근 로직
Guard인증/권한 검사
Interceptor요청/응답 공통 처리
Filter예외 처리
ProducerQueue Job 등록
Worker백그라운드 Job 처리

➕ 5-2. 나쁜 구조

Controller에서:
권한 검사
DB 조회
상태 변경
알림톡 발송
이력 저장
응답 가공
전부 처리

➕ 5-3. 좋은 구조

Controller:
요청을 받고 Service 호출

Service:
상태 변경 규칙과 트랜잭션 처리

Repository:
DB 조회/저장

Producer:
알림톡 Job 등록

Worker:
실제 알림톡 발송

ActionLogService:
관리자 작업 이력 저장
  • 구조가 나뉘면 처음에는 파일이 많아 보이지만, 나중에 수정이 훨씬 쉬워집니다.

✅ 6. Service 메서드가 길어질 때 분리 기준

  • Service 메서드가 길어지는 것은 백엔드에서 흔한 문제입니다.
  • 하나의 메서드 안에 검증, 조회, 상태 변경, 이력 저장, 알림 발송, 응답 가공이 모두 들어가면 수정하기 어려워집니다.

➕ 6-1. 분리 전 예시

async updateStatus(id: number, dto: UpdateStatusDto, adminId: number) {
  const consult = await this.prisma.consult.findUnique({
    where: { id },
  });

  if (!consult) {
    throw new NotFoundException('상담 신청을 찾을 수 없습니다.');
  }

  if (consult.status === 'DONE') {
    throw new BadRequestException('이미 완료된 상담입니다.');
  }

  const updated = await this.prisma.consult.update({
    where: { id },
    data: {
      status: dto.status,
      completedAt: dto.status === 'DONE' ? new Date() : null,
    },
  });

  await this.prisma.consultStatusHistory.create({
    data: {
      consultId: id,
      fromStatus: consult.status,
      toStatus: dto.status,
      adminId,
    },
  });

  if (dto.status === 'DONE') {
    await this.notificationProducer.addConsultDoneJob(id);
  }

  return updated;
}

➕ 6-2. 분리 후 방향

validateConsultExists()
validateStatusTransition()
updateConsultStatusInTransaction()
createStatusHistory()
enqueueStatusChangedNotification()
  • 모든 것을 무조건 함수로 쪼개라는 뜻은 아닙니다.
  • 읽는 사람이 “이 메서드가 어떤 단계를 거치는지” 한눈에 알 수 있어야 합니다.

✅ 7. 상태 변경 로직 리팩토링

  • 상태 변경은 실무에서 가장 리팩토링 가치가 높은 영역입니다.
  • 상태값이 여러 곳에 흩어져 있으면 기능이 커질수록 버그가 늘어납니다.

➕ 7-1. 나쁜 방식

if (status === 'DONE') {
  // 완료 처리
}

if (status === 'CANCELLED') {
  // 취소 처리
}

if (status !== 'PENDING') {
  // 예외 처리
}
  • 문자열 비교가 여러 파일에 흩어집니다.
  • 상태값이 추가되면 수정할 곳이 많아집니다.

➕ 7-2. 좋은 방식

const CONSULT_STATUS_TRANSITIONS = {
  PENDING: ['CALLING', 'DONE', 'CANCELLED'],
  CALLING: ['DONE', 'CANCELLED'],
  DONE: [],
  CANCELLED: [],
} as const;

export function canChangeConsultStatus(
  from: ConsultStatus,
  to: ConsultStatus,
) {
  return CONSULT_STATUS_TRANSITIONS[from].includes(to);
}

➕ 7-3. 사용 예시

if (!canChangeConsultStatus(consult.status, dto.status)) {
  throw new BadRequestException('변경할 수 없는 상태입니다.');
}
  • 상태 전이 규칙을 한 곳에 모으면 테스트하기 쉽습니다.
  • 기획 변경이 생겨도 수정 위치가 명확합니다.

✅ 8. 중복 코드 제거

  • 중복 코드는 기술부채의 대표적인 신호입니다.
  • 같은 로직이 여러 곳에 있으면 하나만 수정하고 다른 곳을 놓치기 쉽습니다.

➕ 8-1. 중복되기 쉬운 로직

전화번호 마스킹
페이지네이션 계산
날짜 범위 계산
권한 검사
상태값 라벨 변환
에러 응답 포맷
S3 파일 key 생성
엑셀 컬럼 정의
외부 API 응답 normalize

➕ 8-2. 공통 유틸 예시

export function getPaginationMeta(params: {
  page: number;
  limit: number;
  total: number;
}) {
  const totalPages = Math.ceil(params.total / params.limit);

  return {
    page: params.page,
    limit: params.limit,
    total: params.total,
    totalPages,
  };
}

➕ 8-3. 주의할 점

  • 너무 빨리 공통화하면 오히려 복잡해질 수 있습니다.
  • 두 번 반복은 지켜보고, 세 번 반복되면 공통화를 검토하는 식으로 접근하면 좋습니다.

✅ 9. 이름 짓기

  • 이름은 코드 품질에서 매우 중요합니다.
  • 좋은 이름은 주석보다 강합니다.

➕ 9-1. 나쁜 이름

const data = await this.getData();
const result = await this.process(data);
const flag = true;
const temp = {};
  • 무엇을 가져오고, 무엇을 처리하는지 알기 어렵습니다.

➕ 9-2. 좋은 이름

const consult = await this.findConsultById(consultId);
const canUpdateStatus = canChangeConsultStatus(
  consult.status,
  nextStatus,
);
const exportJob = await this.createConsultExportJob(query);

➕ 9-3. 실무 네이밍 기준

함수:
동사 + 목적어
createConsult()
updateConsultStatus()
validateAdminPermission()

변수:
의미가 드러나게
consultStatus
exportJob
notificationResult

Boolean:
is, has, can, should
isCompleted
hasPermission
canDownloadExcel
shouldSendNotification
  • AI에게 코드를 맡길 때도 이름이 명확하면 결과물이 더 좋아집니다.

✅ 10. 에러 처리 리팩토링

  • 에러 처리 방식이 기능마다 다르면 프론트엔드와 운영자가 혼란스러워집니다.
  • 공통 에러 코드와 예외 클래스를 정리하면 유지보수가 쉬워집니다.

➕ 10-1. 나쁜 방식

throw new Error('없음');
throw new Error('권한없음');
throw new Error('중복');
  • HTTP status가 명확하지 않습니다.
  • 프론트엔드에서 어떤 에러인지 구분하기 어렵습니다.
  • 로그와 사용자 메시지를 분리하기 어렵습니다.

➕ 10-2. 좋은 방식

throw new NotFoundException({
  code: 'CONSULT_NOT_FOUND',
  message: '상담 신청을 찾을 수 없습니다.',
});

throw new ConflictException({
  code: 'CONSULT_DUPLICATED',
  message: '이미 신청된 정보입니다.',
});

➕ 10-3. 공통 에러 코드 예시

VALIDATION_ERROR
UNAUTHORIZED
FORBIDDEN
CONSULT_NOT_FOUND
CONSULT_DUPLICATED
INVALID_STATUS_TRANSITION
EXPORT_JOB_NOT_FOUND
EXTERNAL_API_FAILED
  • 에러 코드를 정리해두면 API 문서화, 프론트 처리, QA 테스트가 편해집니다.

✅ 11. Repository 분리 기준

  • 작은 프로젝트에서는 Service에서 Prisma를 직접 써도 됩니다.
  • 하지만 DB 조회 조건이 복잡해지고 여러 Service에서 재사용되면 Repository 분리를 고려할 수 있습니다.

➕ 11-1. Service에서 직접 Prisma 사용이 괜찮은 경우

기능이 단순함
조회 조건이 적음
재사용이 거의 없음
팀 규모가 작음
도메인이 아직 자주 바뀜

➕ 11-2. Repository 분리를 고려할 경우

검색/필터 조건이 복잡함
여러 Service에서 같은 조회 사용
select/include 구조가 반복됨
테스트에서 DB 접근을 Mock하고 싶음
DB 접근 로직과 비즈니스 로직이 섞임

➕ 11-3. Repository 예시

@Injectable()
export class ConsultRepository {
  constructor(private readonly prisma: PrismaService) {}

  findById(id: number) {
    return this.prisma.consult.findUnique({
      where: { id },
    });
  }

  findList(query: GetConsultsQueryDto) {
    return this.prisma.consult.findMany({
      where: {
        status: query.status,
        source: query.source,
      },
      orderBy: {
        createdAt: 'desc',
      },
    });
  }
}
  • Repository는 무조건 도입하는 것이 아니라, 복잡도가 올라갈 때 도입하면 됩니다.

✅ 12. DTO와 도메인 로직 분리

  • DTO는 요청/응답 형태를 정의하는 객체입니다.
  • 도메인 로직을 DTO에 너무 많이 넣으면 구조가 흐려질 수 있습니다.

➕ 12-1. DTO의 역할

요청 필드 정의
입력값 검증
Swagger 문서화
타입 변환

➕ 12-2. DTO에 넣지 않는 것이 좋은 것

DB 조회
권한 검사
상태 변경
외부 API 호출
Queue 등록
비즈니스 정책 판단
  • DTO는 입력값의 모양을 담당하고, 실제 정책 판단은 Service나 별도 Policy 함수에서 처리하는 것이 좋습니다.

✅ 13. Config 리팩토링

  • 환경변수가 여러 Service에서 직접 process.env로 사용되면 관리가 어려워집니다.
  • ConfigService나 설정 객체로 모으는 것이 좋습니다.

➕ 13-1. 나쁜 방식

const bucket = process.env.S3_BUCKET;
const apiKey = process.env.KAKAO_API_KEY;
const redisHost = process.env.REDIS_HOST;
  • 오타가 나도 런타임까지 모를 수 있습니다.
  • 어떤 환경변수가 필요한지 추적하기 어렵습니다.

➕ 13-2. 좋은 방식

@Injectable()
export class AppConfigService {
  constructor(private readonly configService: ConfigService) {}

  get s3Bucket() {
    return this.configService.getOrThrow<string>('S3_BUCKET');
  }

  get kakaoApiKey() {
    return this.configService.getOrThrow<string>('KAKAO_API_KEY');
  }

  get redisHost() {
    return this.configService.getOrThrow<string>('REDIS_HOST');
  }
}
  • 필수 환경변수는 서버 시작 시 검증해야 합니다.
  • 설정값 접근 방식이 통일되면 배포 장애가 줄어듭니다.

✅ 14. 큰 리팩토링보다 작은 리팩토링

  • 리팩토링은 한 번에 크게 갈아엎으면 위험합니다.
  • 운영 서비스에서는 작은 단위로 나누어 진행하는 것이 안전합니다.

➕ 14-1. 위험한 리팩토링

전체 Service 구조 한 번에 변경
API 응답 구조 전체 변경
DB 모델명 전체 변경
상태값 enum 전체 변경
프론트/백 동시 대규모 수정

➕ 14-2. 안전한 리팩토링

중복 유틸 하나 분리
상태 전이 함수 분리
에러 코드 정리
DTO 검증 강화
로그 마스킹 함수 적용
Repository 일부 도입
테스트 추가 후 Service 내부 정리
  • 리팩토링도 배포 가능한 작은 단위로 쪼개야 합니다.
  • 변경량이 작을수록 원인 추적과 롤백이 쉽습니다.

✅ 15. 리팩토링 전 테스트 확보

  • 리팩토링은 기능 결과가 바뀌면 안 됩니다.
  • 그래서 핵심 로직에는 테스트가 있으면 좋습니다.

➕ 15-1. 테스트가 필요한 리팩토링 대상

상태 전이 규칙
중복 신청 방지
권한 검사
가격/지원금 계산
날짜 범위 계산
엑셀 Export 조건
알림톡 중복 발송 방지
Webhook 상태 변경

➕ 15-2. 상태 전이 테스트 예시

describe('canChangeConsultStatus', () => {
  it('PENDING에서 DONE으로 변경할 수 있다', () => {
    expect(canChangeConsultStatus('PENDING', 'DONE')).toBe(true);
  });

  it('DONE에서 PENDING으로 변경할 수 없다', () => {
    expect(canChangeConsultStatus('DONE', 'PENDING')).toBe(false);
  });
});
  • 테스트가 있으면 AI가 리팩토링한 코드도 검증하기 쉬워집니다.

✅ 16. 코드 리뷰 기준

  • 혼자 개발하더라도 코드 리뷰 기준은 필요합니다.
  • PR을 만들지 않더라도 배포 전 스스로 확인할 체크리스트를 가져야 합니다.

➕ 16-1. 코드 리뷰 체크리스트

이 함수가 한 가지 책임만 가지는가?
이름만 보고 역할을 알 수 있는가?
중복 코드가 과하지 않은가?
권한 검사가 빠지지 않았는가?
입력값 검증이 있는가?
에러 처리가 일관적인가?
트랜잭션이 필요한 곳에 적용됐는가?
개인정보가 로그에 남지 않는가?
환경변수가 검증되는가?
테스트 또는 수동 QA 기준이 있는가?

➕ 16-2. 1인 개발자 방식

작업 완료
  ↓
git diff 확인
  ↓
위험한 변경 표시
  ↓
테스트/빌드 실행
  ↓
배포 전 체크리스트 확인
  ↓
릴리즈 노트 작성
  • git diff를 보는 습관은 매우 중요합니다.
  • 내가 의도하지 않은 파일 변경이나 로그 출력이 남아 있는지 확인할 수 있습니다.

✅ 17. AI 코드 리뷰 활용

  • AI는 코드 품질 점검에 매우 유용합니다.
  • 다만 전체 코드를 막연히 던지는 것보다 기준을 주고 리뷰시키는 것이 좋습니다.

➕ 17-1. 좋은 질문 예시

다음 NestJS Service 코드를 리뷰해줘.

중점적으로 봐야 할 기준:
1. 책임 분리가 적절한지
2. 상태 변경 로직이 안전한지
3. 트랜잭션이 필요한 곳이 있는지
4. 중복 요청/동시성 문제가 있는지
5. 권한 검사가 빠진 부분이 있는지
6. 개인정보 로그 노출 위험이 있는지
7. 함수명/변수명이 명확한지
8. 테스트하기 어려운 구조가 있는지

원하는 답변:
- 위험한 부분
- 리팩토링 제안
- 우선순위
- 수정 예시

➕ 17-2. AI 리뷰 결과 검증

AI가 실제 프로젝트 정책을 이해했는가?
없는 요구사항을 만들어내지 않았는가?
보안/데이터 정합성 관점이 빠지지 않았는가?
수정 제안이 과하게 복잡하지 않은가?
현재 규모에 맞는 제안인가?
  • AI의 제안은 그대로 믿기보다, 현재 서비스 규모와 운영 리스크를 기준으로 선택해야 합니다.

✅ 18. 리팩토링 작업 기록

  • 리팩토링도 작업 기록으로 남기면 좋습니다.
  • 기능 추가가 아니더라도 운영 안정성과 유지보수성을 높이는 중요한 작업입니다.

➕ 18-1. 커밋 메시지 예시

refactor: 상담 상태 변경 로직을 정책 함수로 분리

refactor: 관리자 권한 검사 Guard 구조 정리

refactor: 알림톡 발송 중복 방지 로직 개선

refactor: ExportJob 처리 상태 검증 함수 분리

➕ 18-2. 작업 기록 예시

## 2026-07-15 리팩토링 기록

### 작업 내용
- 상담 상태 전이 규칙을 `consult-status.policy.ts`로 분리
- Service 내부 중복 if 조건 제거
- 상태 변경 테스트 케이스 추가

### 목적
- 상태값 추가/변경 시 수정 위치를 줄이기 위함
- 잘못된 상태 변경으로 인한 데이터 정합성 문제 방지

### 영향 범위
- 관리자 상담 상태 변경 API
- 상담 상태 변경 이력 저장
- 알림톡 발송 조건

### 검증
- 상태 전이 단위 테스트 통과
- 상담 상태 변경 수동 QA 완료
  • 이런 기록은 나중에 경력기술서에서 “운영 안정성 개선”으로 정리하기 좋습니다.

✅ 19. 리팩토링 우선순위 정하기

  • 리팩토링할 곳은 많지만 시간이 제한되어 있습니다.
  • 가장 위험하거나 자주 수정하는 영역부터 정리해야 합니다.

➕ 19-1. 우선순위 높은 영역

상담 신청 저장
주문/결제 처리
상태 변경
관리자 권한
엑셀 다운로드
알림톡/SMS 발송
Webhook 처리
DB migration 관련 코드

➕ 19-2. 우선순위 낮은 영역

거의 바뀌지 않는 정적 페이지
단순 조회 API
운영 영향이 낮은 내부 유틸
사용 빈도가 낮은 기능

➕ 19-3. 판단 기준

자주 수정하는가?
장애 나면 영향이 큰가?
권한/개인정보/금전과 관련 있는가?
중복 코드가 많은가?
테스트가 어려운가?
AI에게 맡기면 위험한 구조인가?
  • 모든 코드를 예쁘게 만들 필요는 없습니다.
  • 중요한 코드를 안전하게 만드는 것이 먼저입니다.

✅ 20. 실무 체크리스트

➕ 20-1. 코드 품질 체크리스트

  1. 함수와 클래스의 책임이 명확한가?
  2. 이름만 보고 역할을 알 수 있는가?
  3. 중복 코드가 과하지 않은가?
  4. 에러 처리 방식이 일관적인가?
  5. 상태값과 정책 로직이 한 곳에서 관리되는가?
  6. DTO와 비즈니스 로직이 섞이지 않았는가?
  7. 환경변수 접근 방식이 통일되어 있는가?
  8. 민감정보 로그 출력이 없는가?

➕ 20-2. 리팩토링 체크리스트

  1. 기능 결과가 바뀌지 않는 리팩토링인가?
  2. 변경 범위가 너무 크지 않은가?
  3. 테스트 또는 수동 QA 기준이 있는가?
  4. 롤백 가능한 단위인가?
  5. 상태 변경/권한/DB 관련 위험을 확인했는가?
  6. 리팩토링 목적이 명확한가?
  7. 작업 기록을 남겼는가?
  8. 문서나 테스트도 함께 업데이트했는가?

➕ 20-3. 기술부채 관리 체크리스트

  1. 현재 기술부채 목록이 있는가?
  2. 위험한 부채와 괜찮은 부채를 구분했는가?
  3. 보안/권한/개인정보 관련 부채를 우선 처리하는가?
  4. 데이터 정합성 관련 부채를 우선 처리하는가?
  5. 자주 수정하는 영역부터 개선하는가?
  6. 부채 해결 작업을 커밋과 문서로 남기는가?
  7. 새 기능 개발 시 부채가 더 늘어나는지 확인하는가?
  8. AI가 만든 코드를 그대로 쌓아두지 않고 검토하는가?

✅ 21. AI를 활용해 리팩토링할 때 질문법

  • AI에게 리팩토링을 요청할 때는 “깔끔하게 해줘”보다 기준을 구체적으로 줘야 합니다.
  • 특히 운영 서비스에서는 동작 유지, 권한, 트랜잭션, 테스트 가능성, 영향 범위를 같이 봐야 합니다.

➕ 21-1. 좋은 질문 예시

다음 NestJS + Prisma Service 코드를 리팩토링하고 싶어.

서비스 상황:
1. 상담 상태 변경 API에서 사용하는 코드임
2. 상태값은 PENDING, CALLING, DONE, CANCELLED가 있음
3. 상태 변경 시 ConsultStatusHistory를 반드시 저장해야 함
4. DONE으로 변경되면 알림톡 Job을 Queue에 등록함
5. DONE/CANCELLED 이후 일반 관리자는 상태 변경하면 안 됨
6. 같은 상담을 두 관리자가 동시에 변경할 수 있음
7. 개인정보가 로그에 남으면 안 됨

리팩토링 기준:
- 기능 결과는 유지
- 상태 전이 규칙을 별도 함수로 분리
- 트랜잭션 필요한 부분 표시
- 중복 요청/동시성 위험 점검
- 에러 코드를 명확히 정리
- 테스트하기 쉬운 구조로 개선
- 과하게 복잡한 패턴은 피하기

원하는 답변:
1. 현재 코드의 문제점
2. 리팩토링 방향
3. 수정 코드 예시
4. 테스트 케이스
5. 배포 전 QA 체크리스트

➕ 21-2. AI 답변 검증 기준

  1. 기능 결과를 바꾸지 않는가?
  2. 필요 이상으로 복잡한 디자인 패턴을 도입하지 않는가?
  3. 현재 서비스 규모에 맞는가?
  4. 상태 전이, 권한, 트랜잭션을 고려하는가?
  5. Prisma 동시성 문제를 놓치지 않는가?
  6. 개인정보 로그 노출 위험을 확인하는가?
  7. 테스트 케이스를 제안하는가?
  8. 실제 프로젝트 컨벤션과 맞는가?

📌 요약

  • 코드 품질은 코드가 단순히 동작하는지를 넘어, 읽기 쉽고 수정하기 쉽고 장애를 만들 가능성이 낮은 상태를 의미합니다.
  • 리팩토링은 기능 동작은 유지하면서 코드 구조를 개선하는 작업이며, 운영 서비스에서는 작은 단위로 안전하게 진행해야 합니다.
  • 기술부채는 빠른 개발을 위해 남긴 임시 구조가 나중에 유지보수 비용으로 돌아오는 것이며, 보안/권한/개인정보/데이터 정합성 관련 부채는 우선적으로 처리해야 합니다.
  • NestJS 백엔드는 Controller, DTO, Service, Repository, Guard, Interceptor, Filter, Producer, Worker의 책임을 분리하면 유지보수가 쉬워집니다.
  • 상태 변경 로직은 정책 함수로 분리하고, 상태 전이 규칙을 한 곳에서 관리해야 테스트와 변경이 쉬워집니다.
  • 중복 코드는 전화번호 마스킹, 페이지네이션, 날짜 범위, 권한 검사, 상태값 라벨, 에러 응답, S3 key 생성 같은 영역부터 공통화하면 좋습니다.
  • 리팩토링 전에는 핵심 로직 테스트나 수동 QA 기준을 확보해야 하며, 배포 가능한 작은 단위로 나누는 것이 안전합니다.
  • AI는 코드 리뷰와 리팩토링 초안 작성에 유용하지만, 실제 서비스 규모, 권한 정책, 데이터 정합성, 운영 위험을 기준으로 반드시 검수해야 합니다.

0개의 댓글