Skip to content

Conversation

@GoGradually
Copy link
Collaborator

변경된 점

  • API에 대한 버전 넘버링을 path 앞부분에 추가

Copilot AI review requested due to automatic review settings December 31, 2025 08:37
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

이 PR은 API에 버전 관리를 도입하기 위해 경로에 버전 넘버링을 추가하는 변경사항입니다.

  • 기존 PushNotificationController를 복제하여 /v0/push 경로를 가진 PushNotificationControllerV0 생성
  • 푸시 알림 관련 3개 엔드포인트(/vapid, /subscribe, /unsubscribe)에 버전 경로 적용
  • 기존 버전 없는 컨트롤러(/push)는 그대로 유지

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/v0/push")
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제점: API 버전을 "v0"으로 시작하는 것은 일반적이지 않습니다. 통상 API 버전은 v1부터 시작하며, v0는 불안정하거나 실험적인 버전을 의미할 수 있습니다.

영향:

  • API 사용자에게 혼란을 줄 수 있으며, 이 API가 프로덕션 준비가 되지 않았다는 잘못된 인상을 줄 수 있습니다
  • 향후 v1로 마이그레이션 시 불필요한 버전 단계가 추가됩니다

수정 제안:

  • 첫 번째 정식 API 버전은 "/v1/push"로 시작하는 것을 권장합니다
  • 만약 v0가 의도적으로 베타/실험 버전을 의미한다면, @tag description에 이를 명시해야 합니다

Copilot generated this review using guidance from repository custom instructions.
@RestController
@RequestMapping("/v0/push")
@Tag(name = "푸시 알림", description = "푸시 구독 및 VAPID 키 관련 API")
public class PushNotificationControllerV0 {
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제점: 기존 PushNotificationController (경로: /push)와 새로운 PushNotificationControllerV0 (경로: /v0/push)가 동시에 존재하여 두 개의 동일한 API 엔드포인트가 공존합니다.

영향:

  • API 사용자가 어떤 엔드포인트를 사용해야 할지 혼란스러울 수 있습니다
  • 두 컨트롤러가 동일한 기능을 제공하므로 유지보수 부담이 증가합니다
  • 기존 /push 엔드포인트의 생명주기(계속 유지할지, 언제 제거할지)가 불명확합니다

수정 제안:

  • 기존 PushNotificationController@Deprecated 어노테이션을 추가하고, 언제 제거될 예정인지 문서화해야 합니다
  • 또는 기존 컨트롤러를 /v0/push로 리다이렉트하도록 변경하는 것을 고려하세요
  • API 문서에 버전 마이그레이션 가이드를 추가하는 것을 권장합니다

Copilot generated this review using guidance from repository custom instructions.
Comment on lines 3 to 68
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import me.pinitnotification.application.push.PushService;
import me.pinitnotification.domain.member.MemberId;
import me.pinitnotification.interfaces.notification.dto.PushTokenRequest;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/v0/push")
@Tag(name = "푸시 알림", description = "푸시 구독 및 VAPID 키 관련 API")
public class PushNotificationControllerV0 {
private final PushService pushService;

public PushNotificationControllerV0(PushService pushService) {
this.pushService = pushService;
}

@GetMapping("/vapid")
@Operation(
summary = "VAPID 공개키 조회",
description = "웹 푸시 구독에 사용되는 VAPID 공개키를 반환합니다."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "VAPID 공개키",
content = @Content(mediaType = "text/plain", schema = @Schema(implementation = String.class)))
})
public String getVapidPublicKey() {
return pushService.getVapidPublicKey();
}

@PostMapping("/subscribe")
@Operation(
summary = "푸시 토큰 구독 등록",
description = "인증된 회원의 푸시 토큰을 등록합니다."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "구독 등록 완료"),
@ApiResponse(responseCode = "400", description = "토큰이 유효하지 않음", content = @Content)
})
public void subscribe(
@Parameter(hidden = true) @MemberId Long memberId,
@RequestBody PushTokenRequest request) {
pushService.subscribe(memberId, request.token());
}

@PostMapping("/unsubscribe")
@Operation(
summary = "푸시 토큰 구독 해지",
description = "인증된 회원의 등록된 푸시 토큰을 삭제합니다."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "구독 해지 완료"),
@ApiResponse(responseCode = "400", description = "토큰이 유효하지 않음", content = @Content)
})
public void unsubscribe(
@Parameter(hidden = true) @MemberId Long memberId,
@RequestBody PushTokenRequest request) {
pushService.unsubscribe(memberId, request.token());
}


Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제점: 전체 컨트롤러 코드가 기존 PushNotificationController와 완전히 중복됩니다. 두 클래스 간 유일한 차이점은 클래스명과 @RequestMapping 경로뿐입니다.

영향:

  • 코드 중복으로 인해 비즈니스 로직 변경 시 두 곳을 모두 수정해야 합니다
  • 한 쪽만 수정할 경우 버전 간 동작 불일치가 발생할 수 있습니다
  • 유지보수성과 코드 품질이 저하됩니다

수정 제안:

  • 공통 로직을 추상 베이스 클래스로 추출하거나, 컴포지션 패턴을 사용하는 것을 고려하세요
  • 또는 Spring의 @RequestMapping을 조건부로 적용하는 방식으로 단일 컨트롤러를 유지할 수 있습니다
  • 예시: 베이스 컨트롤러에 비즈니스 로직을 구현하고, V0 컨트롤러는 경로 매핑만 담당하도록 구조화
Suggested change
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import me.pinitnotification.application.push.PushService;
import me.pinitnotification.domain.member.MemberId;
import me.pinitnotification.interfaces.notification.dto.PushTokenRequest;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/v0/push")
@Tag(name = "푸시 알림", description = "푸시 구독 및 VAPID 키 관련 API")
public class PushNotificationControllerV0 {
private final PushService pushService;
public PushNotificationControllerV0(PushService pushService) {
this.pushService = pushService;
}
@GetMapping("/vapid")
@Operation(
summary = "VAPID 공개키 조회",
description = "웹 푸시 구독에 사용되는 VAPID 공개키를 반환합니다."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "VAPID 공개키",
content = @Content(mediaType = "text/plain", schema = @Schema(implementation = String.class)))
})
public String getVapidPublicKey() {
return pushService.getVapidPublicKey();
}
@PostMapping("/subscribe")
@Operation(
summary = "푸시 토큰 구독 등록",
description = "인증된 회원의 푸시 토큰을 등록합니다."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "구독 등록 완료"),
@ApiResponse(responseCode = "400", description = "토큰이 유효하지 않음", content = @Content)
})
public void subscribe(
@Parameter(hidden = true) @MemberId Long memberId,
@RequestBody PushTokenRequest request) {
pushService.subscribe(memberId, request.token());
}
@PostMapping("/unsubscribe")
@Operation(
summary = "푸시 토큰 구독 해지",
description = "인증된 회원의 등록된 푸시 토큰을 삭제합니다."
)
@ApiResponses({
@ApiResponse(responseCode = "200", description = "구독 해지 완료"),
@ApiResponse(responseCode = "400", description = "토큰이 유효하지 않음", content = @Content)
})
public void unsubscribe(
@Parameter(hidden = true) @MemberId Long memberId,
@RequestBody PushTokenRequest request) {
pushService.unsubscribe(memberId, request.token());
}
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/v0/push")
@Tag(name = "푸시 알림", description = "푸시 구독 및 VAPID 키 관련 API")
public class PushNotificationControllerV0 extends PushNotificationController {

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings December 31, 2025 08:46
@GoGradually GoGradually merged commit 0a3db12 into master Dec 31, 2025
5 checks passed
@GoGradually GoGradually deleted the feat/api-버전-넘버링-적용 branch December 31, 2025 08:49
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

import me.pinitnotification.interfaces.notification.dto.PushTokenRequest;
import org.springframework.web.bind.annotation.*;

@Deprecated
Copy link

Copilot AI Dec 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제점:
@deprecated 어노테이션이 since, forRemoval 속성 없이 사용되었습니다.

영향:

  • API 사용자가 언제부터 deprecated 되었는지 알 수 없습니다
  • 향후 제거 계획 여부를 파악할 수 없습니다
  • API 마이그레이션 계획을 수립하기 어렵습니다

수정 제안:
since와 forRemoval 속성을 명시하여 deprecated 정책을 명확히 하세요. 예를 들어, 다음과 같이 작성할 수 있습니다:

  • since: API 버전이나 날짜를 명시
  • forRemoval: 향후 제거 계획이 있는 경우 true로 설정

Copilot generated this review using guidance from repository custom instructions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants