You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
CursorSliceResult<T> 추가 — core:common, record(List<T> content, boolean hasNext, Long nextCursor)
CursorSliceResponse<T> 추가 — api:common-api, record + 정적 팩토리 from(CursorSliceResult<T>)
./gradlew :api:common-api:compileJava 통과 확인
./gradlew :bootstrap:test --tests "*ModularityTests" 통과 확인
📁 참고 자료 (선택)
스코프 밖
GlobalExceptionHandler — 만들지 않는다.ApiResponse.error(...)를 호출하는 주체가 이것이라, 이 이슈만으로는 401/403 및 모든 예외가 여전히 공통 포맷으로 나가지 않는다. ApiResponse는 "호출될 준비만 된" 상태가 된다 → 후속 작업 필요
PageResult<T> / PageResponse<T> — 오프셋 페이징 요구가 생길 때
springdoc @Schema / @ApiErrorCode — 저장소에 springdoc 의존성이 없어 애노테이션만 붙일 수 없다. 의존성 도입 작업에서 함께
컨벤션 근거
coding-style.md 2-2절 — ApiResponse 클래스 본문(private 생성자 + 정적 팩토리)
coding-style.md 2-4절 — 커서 응답 필드는 content/hasNext/nextCursor, {X}Result↔{X}Response 쌍
coding-style.md 2-3·2-10·2-11절 — from(...) 변환, 정적 팩토리 네이밍, Lombok 범위
error-handling.md 5절 — error(...) 팩토리 시그니처 3종
architecture.md 101~119줄 — 패키지 배치. common-api는 하위 패키지 없이 {basePackage}
data가 null이어도 JSON에서 생략하지 않는다 — @JsonInclude를 붙이지 않는다. 동치미 ApiResponse.kt에 Jackson 애노테이션이 없어 "data": null이 그대로 직렬화된다. 키가 항상 존재하는 편이 클라이언트 파싱에 예측 가능하다. 애노테이션을 안 쓰므로 Jackson import도 필요 없다.
CursorSliceResponse.content에 방어적 복사를 넣지 않는다 — 동치미 보조 생성자가 cursorSliceResult.content를 toList() 없이 그대로 넘긴다. List.copyOf(...)는 content가 null일 때 NPE가 되고 컨벤션에 규정이 없다.
참고 구현과 다르게 가는 건 @Schema 하나뿐이며, 이는 선택이 아니라 springdoc 의존성이 없어서 생기는 제약이다.
어떤 기능인가요?
api:common-api모듈에 소스 파일이 하나도 없어 모든 API가 공유할 응답 래퍼가 없다. 공통 응답 포맷ApiResponse<T>와 커서 기반 목록 조회 응답CursorSliceResponse<T>를 추가한다.기존
ErrorCode/BusinessException(core:common)을 그대로 받아 성공·실패 응답을 만든다.ErrorCode체계는 새로 만들지 않는다.완료 기준 — 세 타입이 존재하고
CursorSliceResponse.from(CursorSliceResult)로 조회 결과를 응답으로 변환할 수 있다.:api:common-api:compileJava가 통과한다.📝 작업 상세 내용
ApiResponse<T>추가 —api:common-api,kr.ac.kookmin.streamfinal class+@Getter+@RequiredArgsConstructor(access = AccessLevel.PRIVATE)boolean success/String code/String message/T data(전부final)success(T),success(),error(BusinessException),error(ErrorCode),error(String, String)@Accessors(fluent = true)는 붙이지 않는다 (Jackson getter 규약에서 벗어나 프로퍼티가 인식되지 않음)CursorSliceResult<T>추가 —core:common,record(List<T> content, boolean hasNext, Long nextCursor)CursorSliceResponse<T>추가 —api:common-api, record + 정적 팩토리from(CursorSliceResult<T>)./gradlew :api:common-api:compileJava통과 확인./gradlew :bootstrap:test --tests "*ModularityTests"통과 확인📁 참고 자료 (선택)
스코프 밖
GlobalExceptionHandler— 만들지 않는다.ApiResponse.error(...)를 호출하는 주체가 이것이라, 이 이슈만으로는 401/403 및 모든 예외가 여전히 공통 포맷으로 나가지 않는다.ApiResponse는 "호출될 준비만 된" 상태가 된다 → 후속 작업 필요PageResult<T>/PageResponse<T>— 오프셋 페이징 요구가 생길 때@Schema/@ApiErrorCode— 저장소에 springdoc 의존성이 없어 애노테이션만 붙일 수 없다. 의존성 도입 작업에서 함께컨벤션 근거
coding-style.md2-2절 —ApiResponse클래스 본문(private 생성자 + 정적 팩토리)coding-style.md2-4절 — 커서 응답 필드는content/hasNext/nextCursor,{X}Result↔{X}Response쌍coding-style.md2-3·2-10·2-11절 —from(...)변환, 정적 팩토리 네이밍, Lombok 범위error-handling.md5절 —error(...)팩토리 시그니처 3종architecture.md101~119줄 — 패키지 배치.common-api는 하위 패키지 없이{basePackage}참고 구현 (Kotlin)
ApiResponse.kt/CursorSliceResponse.kt참고 구현 기준으로 확정한 항목
동치미 구현을 기준으로 정리했다. 둘 다 "안 한다" 쪽이다.
data가 null이어도 JSON에서 생략하지 않는다 —@JsonInclude를 붙이지 않는다. 동치미ApiResponse.kt에 Jackson 애노테이션이 없어"data": null이 그대로 직렬화된다. 키가 항상 존재하는 편이 클라이언트 파싱에 예측 가능하다. 애노테이션을 안 쓰므로 Jackson import도 필요 없다.CursorSliceResponse.content에 방어적 복사를 넣지 않는다 — 동치미 보조 생성자가cursorSliceResult.content를toList()없이 그대로 넘긴다.List.copyOf(...)는content가null일 때 NPE가 되고 컨벤션에 규정이 없다.참고 구현과 다르게 가는 건
@Schema하나뿐이며, 이는 선택이 아니라 springdoc 의존성이 없어서 생기는 제약이다.