Skip to content

백앤드 코딩 규칙 정리

백앤드에서 규칙 정의가 필요한 항목

Section titled “백앤드에서 규칙 정의가 필요한 항목”

dictionary-api(Java 26 + Spring Boot 4.1) 저장소의 CLAUDE.md/README.md에서 사용 중인 규칙을 기준으로 정리한다.

  1. 디렉토리 규칙
  2. 코딩 컨벤션
  3. 외부 인터페이스 연동 규칙
  4. 예외 처리 규칙
  5. 세션 규칙
  6. 스프링-프로파일 규칙
  7. 로깅 규칙
  8. 암호화 규칙
  9. 배포 규칙
  1. api/<feature>/{controller,dto,repository,service} 레이어드 구조로 기능별 패키지를 구성한다(예- board/dictionary/domain/auth/file/menu/search/tools).
  2. JPA 엔티티는 api/<feature> 하위가 아니라 최상위 entity 패키지에 모은다.
  3. 외부 API 연동 클라이언트는 특정 기능에 종속되지 않는 공용 레이어이므로 최상위 interfaces 패키지(interface는 Java 예약어라 이 이름을 쓴다)에 둔다.
  4. 여러 기능 공통 부품(공통 응답/예외/보안/유틸 등)은 common 패키지에 모은다.
  1. spotless(Google Java Format AOSP, 4칸 들여쓰기)로 포맷을 통일한다. compileJava가 자동으로 spotlessApply를 실행하지만, 커밋 시점(pre-commit)에는 spotlessCheck만 수행해 포맷 문제가 있으면 커밋을 차단한다 — 이 경우 직접 ./gradlew spotlessApply를 실행한 뒤 다시 커밋한다.
  2. 필드(생성자 주입 대상)/메소드 파라미터/로컬 변수/예외 catch 변수/상수는 재할당하지 않는 한 final을 명시한다. spotless가 자동으로 강제하지 않으므로 직접 챙긴다.
  3. DTO 상속: common.dto의 공통 봉투(AbstractDto/RequestDto/ResponseDto) 클래스끼리만 상속하고, 기능별 payload DTO(BoardCreateRequestDto 등)는 부모 없는 순수 POJO로 유지한다. 같은 접두어를 쓰는 payload DTO끼리도 서로 상속하지 않는다.
  4. 응답 DTO(XxxResponseDto)는 그 기능 전용이며 다른 기능이 재사용하지 않는다. 단, 같은 기능 안의 API(find/findAll/findByPage/create/update)끼리는 하나를 공유한다. 반대로 요청 DTO는 API마다 예외 없이 독자적인 파일/클래스를 가진다.
  1. 국립국어원 표준국어대사전(StdictClient)/한영사전(KrdictClient) 오픈API를 Spring RestClient로 호출한다. 인증키는 stdict.api-key/krdict.api-key 프로퍼티로 설정하고 프로파일 설정에 ENC(...)로 암호화해 저장한다.
  2. stdict는 JSON, krdict는 XML만 지원한다 — 두 클라이언트는 응답 포맷 차이를 각자 흡수하고, 호출부(SearchController 등)에는 정규화된 DTO만 노출한다.
  3. 실제 네트워크 호출 없이 MockRestServiceServer로 요청/응답 파싱만 검증하는 단위테스트와, @Tag("integration")으로 분리해 ./gradlew integrationTest로만 수동 실행하는 실제 연동 테스트를 구분한다(하루 호출 제한 50,000건, ./gradlew test/build에는 포함하지 않는다).
  1. 모든 예외는 RestApiExceptionAdvice(@RestControllerAdvice)가 표준 ResponseDto 포맷 (successYn/messageCd)으로 변환해 응답한다. 처리되지 않는 예외까지 잡는 catch-all이 있어 Spring Boot 기본 에러 응답이 새어나가지 않는다.
  2. 결과 코드는 CodeApiResult enum이 HTTP 상태와 1:1로 매핑한다 — 성공 200, 검증 실패/타입 불일치 400(INVALID_PARAMETER), 로그인 필요 401(NOT_LOGIN), 게스트 제한 403(FORBIDDEN), 리소스 없음 404(NOT_FOUND), 그 외 서버 오류 500.
  3. 비즈니스 예외는 BizException으로 던지며, 4xx(정상적인 클라이언트 흐름)는 WARN으로, 실제 서버 오류인 5xx만 ERROR + 스택트레이스로 로그를 남겨 노이즈를 줄인다.
  1. spring-boot-starter-session-jdbc(DB 세션, spring.session.store-type: jdbc)를 사용한다. 세션 스키마도 Flyway로 관리하므로 Spring Session 자체 자동 생성은 꺼둔다.
  2. 세션 만료(8시간)는 로그인 시점부터의 절대 시간이 아니라 마지막 요청으로부터의 유휴 타임아웃이다. 실제 만료 시각은 로그인 응답이 아니라 모든 API 응답의 최상위 ResponseDto.expiresAt(초 단위 epoch)로, 응답이 나갈 때마다 다시 계산해 내려준다.
  3. 로그인 성공 시 같은 계정으로 다른 곳에 열려 있던 이전 세션을 무효화한다(단일 세션 로그인 — t_user.last_session_id + JdbcIndexedSessionRepository.deleteById()로 직접 구현).
  4. 프런트엔드와 도메인이 다른 배포 환경(prod)에서는 세션 쿠키를 SameSite=None/Secure=true로 명시해야 크로스 사이트 호출에 쿠키가 실린다.
  1. application.yaml(공통) + spring.profiles.activelocal/test/prod/docker 중 하나를 선택하고, 각 config/<profile>/application-<profile>.yamlspring.config.import로 로드한다.
  2. local은 실제 MySQL(log4jdbc로 SQL 로깅), test/prod는 인메모리 H2, docker는 컴포즈가 띄우는 MySQL 컨테이너를 바라본다.
  3. H2 콘솔은 test만 켜고 prod는 반드시 꺼둔다 — H2 콘솔은 AuthorizationInterceptor/ SecurityConfig 어느 쪽으로도 보호되지 않아, 켜두면 커밋된 접속정보로 누구나 운영 DB에 접근할 수 있다.
  4. 로깅 설정도 프로파일별 config/<profile>/log4j2-<profile>.yaml로 분리한다.
  1. 기본 spring-boot-starter-logging은 제외하고 log4j2를 사용한다.
  2. 프로파일별로 config/<profile>/log4j2-<profile>.yaml을 분리해 둔다.
  3. docker 프로파일은 파일(RollingFile) 대신 표준출력(Console)으로 남겨 docker compose logs로 확인한다.
  1. 설정값 암호화는 jasypt-spring-boot-starter를 사용하며, DB 비밀번호/외부 API 키 등은 ENC(...) 형태로 프로파일 yaml에 저장한다.
  2. 이 값을 복호화하는 마스터 패스워드(jasypt.encryptor.password)는 어떤 yaml에도 평문 커밋하지 않는다. 공통 설정이 ${JASYPT_ENCRYPTOR_PASSWORD} 환경변수를 참조하고, 실제 값은 실행 환경(로컬 셸/IDE/docker-compose/배포 서버)마다 직접 주입한다.
  3. 이 환경변수가 없으면 애플리케이션 기동 자체가 실패한다(빌드/테스트 시점에는 영향 없음). 실행 주체별(터미널, IDE, docker-compose, 배포 서버)로 각각 설정해야 하며, 배포 환경에는 실제로 배포하기 전에 반드시 먼저 등록한다.
  1. Dockerfile(멀티스테이지 빌드)로 이미지를 만들어 배포 플랫폼(Render)에서 그대로 실행한다.
  2. master가 아니라 deploy 브랜치에 push되면 자동으로 재빌드·재배포된다(브랜치 역할과 병합 규칙 참고).
  3. 배포 환경의 CORS 허용 주소(cors.frontend-uri)가 실제 프런트엔드 배포 주소와 일치해야 하며, 프런트엔드 배포 주소가 바뀌면 함께 갱신한다.
  4. 무료 호스팅 슬립 모드 방지를 위해 헬스체크(GET /actuator/health)를 주기적으로 호출하는 워크플로우를 둔다.
  5. 배포 전 JASYPT_ENCRYPTOR_PASSWORD 환경변수를 배포 서버에 반드시 먼저 등록한다 — 등록 전에 배포하면 애플리케이션이 기동하지 못한다.