단잠 서비스가 궁금하다면
https://ryudain.tistory.com/33
[단잠] 대학교 기숙사 메이트 매칭 및 대학 생활 커뮤니티 서비스 회고
✨ 단잠 / 각기 다른 사람들이 모여 단잠에 들다 ✨ 단잠은 대학교 기숙사 메이트 매칭 및 대학 생활 커뮤니티 서비스입니다. 기숙사 메이트 매칭 외에도사용자의 성향에 맞는다양한 운동, 산
ryudain.tistory.com
서론
단잠 서비스를 개발하면서 주석문에 대한 고찰을 하게 되었습니다.
현재 코드를 보게 된다면
/**
* 모든 유저 신고 내역 조회
* @return
*/
public Map<String, List<ReportResponseDTO>> readReport() {
List<Report> reports = reportRepository.findAll(); // 모든 신고 내역을 가져옵니다.
return reports.stream()
.sorted(Comparator.comparing(Report::getModifiedDateTime).reversed())
.collect(Collectors.groupingBy(report -> report.getUser().getNickname(),
Collectors.mapping(ReportResponseDTO::from, Collectors.toList())));
}
이런 식으로 return 값도 없는 주석이 달려있거나, 주석조차 달려있지도 않은 코드가 많았습니다.
해당 문제를 인식하게 된 것은 앱 출시 전에 개발자들끼리 기능이 제대로 돌아가는 지 테스트를 했을 때 였습니다.
테스트를 진행하고 문제가 발생한 부분들에 대해서 FIX를 하는 과정에서
주석문이 제대로 달려있지 않아서 제가 작성한 코드를 찾고, 수정하는 과정에서 시간이 꽤 걸리게 되었습니다.
제가 작성한 코드를 제가 고치는 과정에서도 이렇게 오래 걸린다면,
다른 개발자분들은 제가 작성한 코드를 한 눈에 이해하기 더욱 어려울 것 같다는 생각이 들었습니다.
Swagger의 사용
저희 팀은 API 명세를 Notion과 Swagger를 통해 진행하고 있었습니다.
아무래도 Notion의 경우엔 저희가 직접 작성하다보니 API URL이 틀리는 경우도 있었고,
그럴때마다 Swagger에서 확인을 하는 경우가 비일비재 했습니다.
하지만, 그렇게 Swageer를 사용하면서 정작 Swagger의 기능들은 사용하지 않고 있음이 생각이 났습니다.
@Tag, @Operation, @ApiResponse, @Parameter 네 가지 어노테이션을 통해 API와 각 API의 파라미터에 상세 정보를 추가하고자 합니다.
기존코드
/**
* 마이 페이지 정보 보기
*
* @param customUserDetails
* @return MyProfileInfoDTO
*/
@GetMapping
public ResponseEntity<MyProfileInfoDTO> readProfile(@AuthenticationPrincipal CustomUserDetails customUserDetails) {
return ResponseEntity.ok(myProfileService.readMyProfileInfo(customUserDetails));
}
수정 된 코드
@Tag(name = "마이 페이지", description = "사용자 마이 페이지 정보 관련 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/user/myProfile")
public class MyProfileController {
private final MyProfileService myProfileService;
@GetMapping
@Operation(
summary = "마이 페이지 정보 보기",
description = "사용자의 마이 페이지 정보를 반환합니다."
)
@ApiResponse(responseCode = "200", description = "성공")
@ApiResponse(responseCode = "400", description = "파라미터 오류")
public ResponseEntity<MyProfileInfoDTO> readProfile(
@Parameter(description = "인증된 사용자 정보")
@AuthenticationPrincipal CustomUserDetails customUserDetails) {
return ResponseEntity.ok(myProfileService.readMyProfileInfo(customUserDetails));
}
}
사용하는 어노테이션의 의미는 다음과 같습니다.
@Tag
@Tag는 API의 특정 범주를 정의합니다.
API 문서에서 특정 기능을 구분하는 데 사용합니다.
주요 용도: API 그룹화 (예: 마이 페이지, 유저 관리 등)
@Operation
@Operation은 개별 API 엔드포인트에 대한 정보를 설명합니다.
summary는 간단한 설명을 제공하고, description은 보다 자세한 설명을 제공합니다.
각 API 메소드에 대한 설명을 Swagger UI에서 볼 수 있습니다.
주요 용도: API 메소드의 목적과 세부사항을 설명
@ApiResponse
@ApiResponse는 API 응답 코드에 대한 설명을 제공합니다.
각 HTTP 응답 상태 코드에 대해 설명을 추가할 수 있으며, responseCode와 description 속성을 사용하여 응답에 대한 정보를 정의합니다.
주요 용도: HTTP 상태 코드에 대한 설명을 명시
@Parameter
@Parameter는 메소드 파라미터에 대한 설명을 제공합니다.
API 문서에서 각 파라미터가 무엇을 의미하는지 설명할 수 있으며, description, required, example 등을 포함할 수 있습니다.
주요 용도: 메소드 파라미터의 의미와 용도 설명
비즈니스 로직 메소드
비즈니스 로직에 존재하는 메소드들의 경우 제대로 된 메소드의 명명으로 해당 메소드를 한눈에 파악하면 좋겠지만, 그럼에도 가독성이 떨어질 것이라고 생각했습니다.
해당 문제에 대해서 주변에 자문을 구하였고, 커스텀 어노테이션을 통한 메소드 설명을 작성하고자 합니다.
커스텀 어노테이션 제작 코드
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) // 어노테이션을 메소드에 적용
@Retention(RetentionPolicy.RUNTIME) // 런타임 동안 어노테이션 정보를 유지
public @interface MethodDescription {
// description을 통해 메소드 설명 추가
public String description();
}
기존 코드
맨 처음에 보여드렸던 문제점이 있는 메소드의 코드입니다.
/**
* 마이 페이지 정보 보기
*
* @param customUserDetails
* @return MyProfileInfoDTO
*/
@GetMapping
public ResponseEntity<MyProfileInfoDTO> readProfile(@AuthenticationPrincipal CustomUserDetails customUserDetails) {
return ResponseEntity.ok(myProfileService.readMyProfileInfo(customUserDetails));
}
수정 된 코드
아래와 같이 커스텀 어노테이션을 적용하여 Method의 설명을 직관적으로 표현하고, 보다 클린한 코드가 작성되도록 수정하였습니다.
public class ReportService {
@MethodDescription(description = "모든 유저 신고 내역 조회")
public Map<String, List<ReportResponseDTO>> readReport() {
List<Report> reports = reportRepository.findAll(); // 모든 신고 내역을 가져옵니다.
return reports.stream()
.sorted(Comparator.comparing(Report::getModifiedDateTime).reversed())
.collect(Collectors.groupingBy(report -> report.getUser().getNickname(),
Collectors.mapping(ReportResponseDTO::from, Collectors.toList())));
}
}
마치며
단잠은 초창기에 전반적인 컨벤션이 지정되지 않은 상태로 기능 구현만 급급하게 했던 것 같습니다.
그로인해 고쳐나가야할 부분들이 수정을 진행할 때마다 나오고 있는 것 같습니다.
초기 설계 작업이 얼마나 중요한지 매번 깨닫고 있습니다 ................
이렇게 수정하고, 컨벤션을 팀끼리 맞춰가면서 이후에 있을 프로젝트에선 초창기 설계 방향에 대해 알 수 있다고 생각하여 뜻깊은 시간이 되고 있다고 생각합니다. . ..
그리고 Swagger 어노테이션을 사용하다보니, 생각보다 너무 많은 어노테이션이 사용된다고 생각합니다.
이에따라서 다른 방안이 있는 지 더 고찰해볼 것 같습니다.