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