Files
api2/docs/code-conventions.md
T

81 lines
10 KiB
Markdown

# 코드 컨벤션
## 파일 생성 규칙
- 새 기능은 가능하면 `modules/{도메인명}Controller` 단위로 패키지를 만들고 그 아래에 `service`, `mapper`, `dto`, `vo`를 필요한 만큼만 추가한다.
- 요청 검증이나 JSON 바인딩이 필요하면 `form` 패키지를 함께 만든다.
- 클래스명은 역할이 바로 드러나게 `도메인명 + 역할` 형식을 유지한다. 예: `ApiService`, `ApiMapper`, `ApiVo`
- Mapper 인터페이스를 추가하면 같은 이름의 XML을 `src/main/resources/mapper/{도메인경로}` 아래 함께 만든다.
- 단순 예시 코드와 운영 코드는 섞지 말고 패키지로 분리한다.
- 설정성 클래스는 `config` 하위 역할별 패키지에 둔다. 예: `config.jwt`, `config.exception`, `config.properties`
## DTO/VO/Form 규칙
- `Dto`는 각 Controller 에서 Service, Mapper 로 전달하는 파라미터 성격의 값 객체로 정의한다.
- `Dto`는 요청 처리에 필요한 저장, 수정, 로그 적재 같은 작업 파라미터를 담는 용도로 우선 사용한다.
- `Vo`는 Mapper 에서 Service, Controller 로 반환하는 값 객체로 정의한다.
- `Vo`는 Mapper 조회 결과를 담을 수 있고, Service 에서 비즈니스 로직 처리 후 필요한 데이터를 가공해서 Controller 로 반환하는 용도로 사용한다.
- `Form`은 Controller 입력 검증과 요청 바인딩 전용으로 두고, `@Valid` 와 Jakarta Validation 어노테이션을 우선 사용한다.
- Form/Dto/Vo 클래스명은 가능한 한 Controller 메서드명을 기준으로 맞춘다. 예: Controller 메서드가 `adminMemberList` 이면 `AdminMemberListForm`, `AdminMemberListDto`, `AdminMemberListVo` 를 사용한다.
- 신규 API 에서는 path 동작명과 다른 표현을 지양한다. 예: 목록은 `Search` 보다 `List`, 상세는 `Detail` 보다 `View`, 수정은 `Update` 보다 `Modify` 를 우선 사용한다.
- 현재 코드처럼 DTO/VO는 Lombok `@Getter`, 필요한 경우에만 `@Setter`를 사용한다.
- Form 안에는 DTO 변환 메서드를 둘 수 있으며, 짝이 되는 Dto 로 변환하는 메서드는 `toDto()` 를 기본으로 사용한다. 예: `AdminMemberListForm#toDto()``AdminMemberListDto` 를 반환한다.
- 하나의 Form 이 여러 Dto 로 변환되어야 하는 예외 상황에서만 `toAdminMemberListDto()` 처럼 대상 Dto 이름을 명시한다.
- Form 에서 DTO 로 변환할 때는 검증과 `trim()`, 기본값 치환까지 같이 처리하는 현재 패턴을 우선 따른다.
- DTO 안에 연관된 다른 DTO 변환이 꼭 필요할 때만 최소한의 보조 메서드를 둔다.
- 외부 응답에 노출되면 안 되는 내부 필드는 응답에 사용될 수 있는 객체에서 `@JsonIgnore`로 숨긴다.
- 등록, 수정, 삭제, 상태 변경처럼 처리 여부만 반환하는 VO 의 boolean 필드는 `processed` 로 통일한다. 예: `private boolean processed;`
- 목록 조회 API 는 특별한 사유가 없으면 기본적으로 페이징을 적용하고, 목록 조회 Form/Dto 는 `PageRequest`, 목록 응답 Vo 는 `PageResponse` 를 상속한다.
## Swagger/OpenAPI 문서화 규칙
- 신규 또는 수정되는 Controller, Form, VO에는 Swagger 테스트 편의성을 위한 설명을 반드시 남긴다.
- Controller 클래스에는 `@Tag`를 사용해 API 그룹명과 설명을 작성한다.
- Controller 메서드에는 `@Operation`으로 `summary``description`을 작성한다.
- `@RequestBody` Form 클래스에는 클래스 레벨 `@Schema(description = "...")`를 작성한다.
- `@ModelAttribute` 검색 Form 필드에도 `@Schema`를 작성해 Swagger query parameter 설명과 예시가 보이게 한다.
- Form 필드에는 `@Schema``description`, `example`을 작성한다.
- 필수 입력값은 Jakarta Validation 어노테이션과 `@Schema(requiredMode = Schema.RequiredMode.REQUIRED)`를 함께 사용한다.
- 코드값 필드는 허용 값을 `description`에 명시한다. 예: `userType: A 관리자, T 교사, S 학생`, `withdrawStatus: N 정상, P 탈퇴대기, Y 탈퇴완료`.
- VO 클래스에는 클래스 레벨 `@Schema(description = "...")`를 작성한다.
- VO 필드에는 `@Schema`로 응답 값의 의미를 작성한다.
- 토큰, 임시 비밀번호처럼 Swagger 테스트에 필요한 응답 필드는 VO에 명시하되 `description`에 용도를 적는다.
- 비밀번호, DB 조회용 내부 식별자, `resultCode`처럼 외부 응답에 노출하지 않는 필드는 `@JsonIgnore`를 사용하고 필요하면 `@Schema(hidden = true)`도 함께 사용한다.
- 클라이언트가 직접 전달하지 않는 내부 계산 필드나 서버 전용 getter는 `@Schema(hidden = true)`로 숨긴다.
- 페이징 요청에서 클라이언트는 `page`, `size`만 전달하고, `limit`, `offset`은 Swagger에 노출하지 않는다.
- 페이징 응답의 `rowStartNum`처럼 계산 방식이 필요한 값은 프론트 사용 방법까지 `description`에 남긴다.
## Mapper/MyBatis 규칙
- Service/Mapper 메서드명은 주된 DB 동작이 드러나도록 `select`, `insert`, `update` 접두어를 먼저 붙이고 Controller 메서드명을 이어서 만든다. 예: Controller 메서드가 `adminSsoClientList` 이면 Service/Mapper 조회 메서드는 `selectAdminSsoClientList`.
- 삭제 API도 기본적으로 물리 삭제가 아니라 상태 변경으로 처리하므로 실제 SQL 동작에 맞춰 `update...Delete` 형태를 우선 사용한다. 예: Controller 메서드가 `adminSsoClientDelete` 이면 Service/Mapper 메서드는 `updateAdminSsoClientDelete`.
- 물리 삭제가 정책상 명확히 필요한 예외 상황에서만 `delete` SQL과 `delete...` 메서드명을 사용한다.
- SQL 을 여러 줄로 작성할 때 콤마는 다음 줄 앞에 둔다. 예: `SELECT COL1`, 다음 줄 `, COL2`.
- Mapper XML `namespace`는 인터페이스의 전체 경로와 정확히 일치시킨다.
- XML의 `id`는 Mapper 메서드명과 동일하게 맞춘다.
- 조회 결과 타입은 `resultType`, 저장/수정 파라미터는 DTO 필드명과 매핑되는 프로퍼티명을 그대로 사용한다.
- Mapper XML 안 SQL 블록 시작부 주석은 현재 코드처럼 `/*Mapper.method*/` 형식을 유지한다.
- Mapper XML 위치는 `src/main/resources/mapper/**/*.xml` 이다.
- `map-underscore-to-camel-case: true` 설정으로 DB 컬럼 `user_idx` 는 Java 필드 `userIdx` 로 자동 매핑된다.
- `useGeneratedKeys`, `keyProperty` 를 사용하는 insert 가 있으므로 신규 PK 생성 테이블은 현재 패턴을 먼저 확인한다.
- 상태 집계나 이력성 데이터는 단건 update 외에 이벤트 로그 insert 가 같이 필요한지 확인한다.
## Java/Spring 코드 스타일
- Java 코드는 현재 프로젝트 스타일에 맞춰 탭/들여쓰기와 import 정렬을 유지한다.
- 코드는 초급 개발자가 읽어도 흐름을 따라갈 수 있도록 직관적으로 작성한다. 과한 축약, 기교적인 표현, 불필요하게 복잡한 체이닝보다 명확한 변수명과 단계적인 흐름을 우선한다.
- Java 코드에서 메서드 호출, 어노테이션 인자, 생성자 인자 등을 여러 줄로 작성할 때 콤마는 다음 줄 앞에 둔다. 예: `summary = "회원 목록 조회"` 다음 줄 `, description = "..."`
- 기능 수정/추가 시 가능하면 Red-Green-Refactor 흐름을 따른다. 먼저 실패하는 테스트나 재현 가능한 검증 조건을 만들고(Red), 최소 구현으로 통과시키며(Green), 이후 컨벤션과 가독성에 맞게 정리한다(Refactor). 테스트 작성이 어려운 경우에는 최소 실행 검증 절차를 먼저 정하고 결과를 보고한다.
- Lombok은 반복 보일러플레이트 제거에만 절제해서 사용한다.
- 로그는 `Slf4j`를 사용하고 반복문 내부 대량 출력은 지양한다.
- 새로운 기능은 가능하면 역할이 드러나는 패키지로 분리한다.
- 생성자 주입을 기본으로 하고 필드 주입은 추가하지 않는다.
- Service 에서 DB 상태를 바꾸는 메서드는 필요한 범위에서 `@Transactional`을 사용하고, 조회 전용은 `readOnly = true`를 우선 검토한다.
- 문자열 입력값은 현재 코드처럼 필요한 지점에서 `trim()` 처리하고, null 가능성 여부를 먼저 확인한다.
- 지역 변수명은 `result`, `list`, `vo`, `dto` 처럼 범용적인 이름보다 도메인과 객체 종류가 드러나게 작성한다. 예: `List<AdminMemberVo> adminMemberList`, `AdminMemberListVo adminMemberListVo`, `AdminMemberListDto adminMemberListDto`.
- 같은 타입 또는 같은 의미의 변수가 여러 개 필요해도 객체 종류를 유지하고, 구분이 필요한 경우 숫자 suffix 를 제한적으로 사용한다. 예: `AdminMemberVo adminMemberVo1`, `AdminMemberVo adminMemberVo2`.
## Controller/Service 규칙
- Controller 는 요청/응답 조립과 인증 주체 확인에 집중하고, DB 처리나 복잡한 계산은 Service 로 넘긴다.
- Controller 응답은 `ResponseEntity<ApiResponse<T>>` 를 기본으로 사용한다. 파일 다운로드처럼 바이너리 응답이 필요한 경우만 예외로 둔다.
- Controller 에서는 `@RequestBody`, `@PathVariable`, `@RequestHeader`, `@CookieValue` 를 명시적으로 선언해 요청 출처를 드러낸다.
- 인증 사용자 확인은 모듈 구현에 따라 `HttpSession` 또는 `SecurityContextHolder` 를 사용하므로 기존 방식을 먼저 맞춘다.
- 목록 조회는 Controller 에서 `@ModelAttribute` 검색 Form 을 받고, Service 에서 `totalCount` 와 목록을 조회한 뒤 `PageResponse#setPaging(...)` 으로 페이징 정보를 채운다.
- 신규 API path 의 동작명은 CRUD 성격에 맞춰 통일한다. 목록은 `list`, 상세 조회는 `view`, 등록은 `add`, 수정은 `modify`, 삭제는 `delete` 를 사용한다.
- Controller 메서드명은 가능한 한 path 조합을 기준으로 만든다. 클래스 레벨 path 와 메서드 레벨 path 를 이어 붙인 의미가 드러나게 작성한다. 예: 클래스 path 가 `/admin/member`, 메서드 path 가 `/list` 이면 `adminMemberList`, 클래스 path 가 `/admin/sso/client`, 메서드 path 가 `/view` 이면 `adminSsoClientView`.