Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[REFACTOR] swagger 관련 코드 controller에서 분리 #141

Merged
merged 7 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.blackcompany.eeos.auth.application.usecase.LoginUsecase;
import com.blackcompany.eeos.auth.application.usecase.ReissueUsecase;
import com.blackcompany.eeos.auth.application.usecase.WithDrawUsecase;
import com.blackcompany.eeos.auth.presentation.docs.AuthApi;
import com.blackcompany.eeos.auth.presentation.support.AuthConstants;
import com.blackcompany.eeos.auth.presentation.support.Member;
import com.blackcompany.eeos.auth.presentation.support.TokenExtractor;
Expand All @@ -16,7 +17,6 @@
import com.blackcompany.eeos.common.presentation.respnose.ApiResponseGenerator;
import com.blackcompany.eeos.common.presentation.respnose.MessageCode;
import com.blackcompany.eeos.common.presentation.support.CookieManager;
import io.swagger.v3.oas.annotations.Operation;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Qualifier;
Expand All @@ -27,7 +27,7 @@

@RestController
@RequestMapping("/api/auth")
public class AuthController {
public class AuthController implements AuthApi {
private final LoginUsecase loginUsecase;
private final ReissueUsecase reissueUsecase;
private final TokenExtractor tokenExtractor;
Expand All @@ -53,11 +53,9 @@ public AuthController(
this.withDrawUsecase = withDrawUsecase;
}

@Operation(
summary = "로그인을 한다.",
description = "PathVariable에 담긴 redirect_url, code를 받아 액세스 토큰과 리프레시 토큰을 발급한다.")
@Override
@PostMapping("/login/{oauthServerType}")
ApiResponse<SuccessBody<TokenResponse>> login(
public ApiResponse<SuccessBody<TokenResponse>> login(
@PathVariable String oauthServerType,
@RequestParam("code") String code,
@RequestParam("redirect_uri") String uri,
Comment on lines 57 to 61
Copy link
Contributor

Choose a reason for hiding this comment

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

이전에 Slack으로 보내주신 사진을 보면, RequestMapping과 PathVariable, RequestParam도 모두 Interface로 빼놓았는데, 지금은 안 빼놓으신 이유가 Controller 메소드 코드를 봤을 때, 어떻게 동작하는지 바로 알 수 있게 하기 위함인가요?!

Copy link
Contributor Author

@kssumin kssumin Aug 21, 2024

Choose a reason for hiding this comment

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

넵!! 맞아요!!

RequestMapping과 PathVariable, RequestParam은 web에 의존적인 것이라고 판단했어요!!

swagger가 아닌 다른 문서로 변경되었을 때 web 코드가 변경되어야 하는가?

만약 추후 swagger가 아닌 다른 문서로 변경이 된다고 했을 때 RequestMapping과 PathVariable, RequestParam와 같은 web 계층의 변화는 없어야 한다고 생각했어요.

해당 라이브러리들의 패키지를 기준

  • RequestMapping과 PathVariable, RequestParam는 package org.springframework.web.bind.annotation; 패키지에 속한다.
  • 즉 web 계층에 의존적이라고 생각했습니다.
image
  • 반면 swagger 어노테이션은 package io.swagger.v3.oas.annotations;에 속한다.
  • 즉 swagger 에 의존적이라고 생각했습니다.
image

이러한 패키지의 위치를 기준으로 interface로 나누었습니다.

Copy link
Contributor

Choose a reason for hiding this comment

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

아 그렇네요 Swagger에 Web 어노테이션을 붙이면, Web 어노테이션이 Swagger와 공존해서, 의존성이 생겨버리니까 분리를 한 거군요! 이해됐습니다! 감사합니다!

Expand All @@ -68,18 +66,18 @@ ApiResponse<SuccessBody<TokenResponse>> login(
return ApiResponseGenerator.success(response, HttpStatus.CREATED, MessageCode.CREATE);
}

@Operation(summary = "로그인 한다.", description = "사용자가 id와 password를 이용하여 로그인한다.")
@Override
@PostMapping("/login")
ApiResponse<SuccessBody<TokenResponse>> login(
public ApiResponse<SuccessBody<TokenResponse>> login(
@RequestBody EEOSLoginRequest request, HttpServletResponse httpResponse) {
TokenModel tokenModel = loginUsecase.login(request.getId(), request.getPassword());
TokenResponse response = generateTokenResponse(tokenModel, httpResponse);
return ApiResponseGenerator.success(response, HttpStatus.CREATED, MessageCode.CREATE);
}

@Operation(summary = "토큰을 재발급한다.", description = "쿠키에 담긴 사용자 토큰을 이용하여 리프레시 토큰을 반환한다.")
@Override
@PostMapping("/reissue")
ApiResponse<SuccessBody<TokenResponse>> reissue(
public ApiResponse<SuccessBody<TokenResponse>> reissue(
HttpServletRequest request, HttpServletResponse httpResponse) {
String token = tokenExtractor.extract(request);
TokenModel tokenModel = reissueUsecase.execute(token);
Expand All @@ -88,9 +86,9 @@ ApiResponse<SuccessBody<TokenResponse>> reissue(
return ApiResponseGenerator.success(response, HttpStatus.CREATED, MessageCode.CREATE);
}

@Operation(summary = "로그아웃한다.", description = "쿠키에 담긴 리프레시 토큰을 이용하여 로그아웃한다.")
@Override
@PostMapping("/logout")
ApiResponse<SuccessBody<Void>> logout(
public ApiResponse<SuccessBody<Void>> logout(
HttpServletRequest request, HttpServletResponse httpResponse, @Member Long memberId) {
String token = tokenExtractor.extract(request);
logOutUsecase.logOut(token, memberId);
Expand All @@ -99,9 +97,9 @@ ApiResponse<SuccessBody<Void>> logout(
return ApiResponseGenerator.success(HttpStatus.OK, MessageCode.DELETE);
}

@Operation(summary = "회원탈퇴", description = "쿠키에 담긴 리프레시 토큰을 이용하여 회원을 탈퇴한다.")
@Override
@PostMapping("/withdraw")
ApiResponse<SuccessBody<Void>> withDraw(
public ApiResponse<SuccessBody<Void>> withDraw(
HttpServletRequest request, HttpServletResponse httpResponse, @Member Long memberId) {
String token = tokenExtractor.extract(request);
withDrawUsecase.withDraw(token, memberId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.blackcompany.eeos.auth.presentation.docs;

import com.blackcompany.eeos.auth.application.dto.request.EEOSLoginRequest;
import com.blackcompany.eeos.auth.application.dto.response.TokenResponse;
import com.blackcompany.eeos.auth.presentation.support.Member;
import com.blackcompany.eeos.common.presentation.respnose.ApiResponse;
import com.blackcompany.eeos.common.presentation.respnose.ApiResponseBody.SuccessBody;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

@Tag(name = "인증", description = "인증 관련 API")
public interface AuthApi {
@Operation(
summary = "OAuth 로그인",
description = "PathVariable에 담긴 redirect_url, code를 받아 액세스 토큰과 리프레시 토큰을 발급한다.")
ApiResponse<SuccessBody<TokenResponse>> login(
@Parameter(description = "OAuth 서버 타입 (예: slack)", required = true) @PathVariable
String oauthServerType,
@Parameter(description = "OAuth 인증 코드", required = true) @RequestParam("code") String code,
@Parameter(description = "리다이렉트 URI", required = true) @RequestParam("redirect_uri")
String uri,
HttpServletResponse httpResponse);

@Operation(summary = "일반 로그인", description = "사용자가 id와 password를 이용하여 로그인한다.")
ApiResponse<SuccessBody<TokenResponse>> login(
@Parameter(description = "로그인 요청 정보", required = true) @RequestBody EEOSLoginRequest request,
HttpServletResponse httpResponse);

@Operation(
summary = "토큰 재발급",
description = "쿠키에 담긴 사용자 토큰을 이용하여 리프레시 토큰을 반환한다.",
security = @SecurityRequirement(name = "bearerAuth"))
ApiResponse<SuccessBody<TokenResponse>> reissue(
HttpServletRequest request, HttpServletResponse httpResponse);

@Operation(
summary = "로그아웃",
description = "쿠키에 담긴 리프레시 토큰을 이용하여 로그아웃한다.",
security = @SecurityRequirement(name = "bearerAuth"))
ApiResponse<SuccessBody<Void>> logout(
HttpServletRequest request,
HttpServletResponse httpResponse,
@Parameter(hidden = true) @Member Long memberId);

@Operation(
summary = "회원탈퇴",
description = "쿠키에 담긴 리프레시 토큰을 이용하여 회원을 탈퇴한다.",
security = @SecurityRequirement(name = "bearerAuth"))
ApiResponse<SuccessBody<Void>> withDraw(
HttpServletRequest request,
HttpServletResponse httpResponse,
@Parameter(hidden = true) @Member Long memberId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import com.blackcompany.eeos.comment.application.model.CommentModel;
import com.blackcompany.eeos.member.application.exception.NotFoundMemberException;
import com.blackcompany.eeos.member.persistence.MemberRepository;

import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -63,7 +62,10 @@ private String findMemberName(Long memberId) {
return memberRepository.findById(memberId).orElseThrow(NotFoundMemberException::new).getName();
}

private String getCreateTimeString(CommentModel model){
return model.getCreatedDate().toLocalDateTime().format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH시 mm분 ss초"));
private String getCreateTimeString(CommentModel model) {
return model
.getCreatedDate()
.toLocalDateTime()
.format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH시 mm분 ss초"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@

public class NotCreateAdminCommentException extends BusinessException {

private static final String FAIL_CODE = "";
private static final String FAIL_CODE = "";

public NotCreateAdminCommentException(){
super(FAIL_CODE, HttpStatus.BAD_REQUEST);
}

@Override
public String getMessage() { return "관리자는 코멘트를 작성할 수 없습니다."; }
public NotCreateAdminCommentException() {
super(FAIL_CODE, HttpStatus.BAD_REQUEST);
}

@Override
public String getMessage() {
return "관리자는 코멘트를 작성할 수 없습니다.";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@

public class NotExpectedCommentEditException extends BusinessException {

private static final String FAIL_CODE = "8006";
private static final String FAIL_CODE = "8006";

public NotExpectedCommentEditException(){
super(FAIL_CODE, HttpStatus.EXPECTATION_FAILED);
}

@Override
public String getMessage(){ return "코멘트 수정 중 예상치 못한 에러가 발생하였습니다."; }
public NotExpectedCommentEditException() {
super(FAIL_CODE, HttpStatus.EXPECTATION_FAILED);
}

@Override
public String getMessage() {
return "코멘트 수정 중 예상치 못한 에러가 발생하였습니다.";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ public void validateDelete(Long memberId) {
}

public boolean isSuperComment() {
return superCommentId.equals(Long.valueOf(-1L));
return superCommentId.equals(-1L);
}

public void changeSuperComment(Long newSuper){
public void changeSuperComment(Long newSuper) {
this.superCommentId = newSuper;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,10 @@ private void deleteComment(Long commentId) {

private CommentModel createComment(CommentModel model) {

if(!model.isSuperComment()) {
if (!model.isSuperComment()) {
CommentModel parentsComment = findCommentById(model.getSuperCommentId());
if (!parentsComment.isSuperComment()) changeSuperComment(model); // 이 코드를 추가한 이유, 답글에 답글을 달았을 때 보이지 않는다.
if (!parentsComment.isSuperComment())
changeSuperComment(model); // 이 코드를 추가한 이유, 답글에 답글을 달았을 때 보이지 않는다.
}

CommentEntity entity = commentEntityConverter.toEntity(model);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

import io.lettuce.core.dynamic.annotation.Param;
import java.util.List;
import java.util.Optional;
import javax.transaction.Transactional;

import lombok.NonNull;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
Expand All @@ -24,8 +22,7 @@ List<CommentEntity> findCommentByProgramIdAndPresentingTeamId(
@Transactional
@Modifying(clearAutomatically = true)
@Query("UPDATE CommentEntity c SET c.content=:content WHERE c.id=:commentId")
int updateById(
@Param("commentId") Long commentId, @Param("content") String content);
int updateById(@Param("commentId") Long commentId, @Param("content") String content);

@Transactional
@Modifying
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.blackcompany.eeos.comment.presentation;
package com.blackcompany.eeos.comment.presentation.controller;

import com.blackcompany.eeos.auth.presentation.support.Member;
import com.blackcompany.eeos.comment.application.dto.*;
Expand All @@ -8,11 +8,11 @@
import com.blackcompany.eeos.comment.application.usecase.DeleteCommentUsecase;
import com.blackcompany.eeos.comment.application.usecase.GetCommentUsecase;
import com.blackcompany.eeos.comment.application.usecase.UpdateCommentUsecase;
import com.blackcompany.eeos.comment.presentation.docs.CommentApi;
import com.blackcompany.eeos.common.presentation.respnose.ApiResponse;
import com.blackcompany.eeos.common.presentation.respnose.ApiResponseBody.*;
import com.blackcompany.eeos.common.presentation.respnose.ApiResponseGenerator;
import com.blackcompany.eeos.common.presentation.respnose.MessageCode;
import io.swagger.v3.oas.annotations.Operation;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
Expand All @@ -22,7 +22,7 @@
@RestController
@RequestMapping("api/comments")
@RequiredArgsConstructor
public class CommentController {
public class CommentController implements CommentApi {

private final CreateCommentUsecase createCommentUsecase;
private final UpdateCommentUsecase updateCommentUsecase;
Expand All @@ -31,17 +31,17 @@ public class CommentController {
private final CommentResponseConverter responseConverter;
private final CommentResponseConverter commentResponseConverter;

@Override
@PostMapping
@Operation(summary = "질문 및 코멘트 작성", description = "코멘트 및 질문을 생성합니다.")
public ApiResponse<SuccessBody<CommandCommentResponse>> create(
@Member Long memberId, @RequestBody CreateCommentRequest request) {
CommentModel model = createCommentUsecase.create(memberId, request);
return ApiResponseGenerator.success(
responseConverter.from(model), HttpStatus.OK, MessageCode.CREATE);
}

@Override
@PutMapping("/{commentId}")
@Operation(summary = "질문 및 코멘트 수정", description = "코멘트 및 질문을 수정합니다.")
public ApiResponse<SuccessBody<CommandCommentResponse>> update(
@Member Long memberId,
@PathVariable("commentId") Long commentId,
Expand All @@ -51,16 +51,16 @@ public ApiResponse<SuccessBody<CommandCommentResponse>> update(
responseConverter.from(model), HttpStatus.OK, MessageCode.UPDATE);
}

@Override
@DeleteMapping("/{commentId}")
@Operation(summary = "질문 및 코멘트 삭제", description = "코멘트 및 질문을 삭제합니다.")
public ApiResponse<SuccessBody<Void>> delete(
@Member Long memberId, @PathVariable("commentId") Long commentId) {
deleteCommentUsecase.delete(memberId, commentId);
return ApiResponseGenerator.success(HttpStatus.OK, MessageCode.DELETE);
}

@Override
@GetMapping
@Operation(summary = "질문 및 코멘트 조회", description = "코멘트 및 질문을 조회합니다.")
public ApiResponse<SuccessBody<QueryCommentsResponse>> getComments(
@Member Long memberId,
@RequestParam("programId") Long programId,
Expand All @@ -74,6 +74,7 @@ public ApiResponse<SuccessBody<QueryCommentsResponse>> getComments(
memberId, e, getCommentsUsecase.getAnswerComments(e.getId())))
.collect(Collectors.toList());

return ApiResponseGenerator.success(commentResponseConverter.from(responses), HttpStatus.OK, MessageCode.GET);
return ApiResponseGenerator.success(
commentResponseConverter.from(responses), HttpStatus.OK, MessageCode.GET);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.blackcompany.eeos.comment.presentation.docs;

import com.blackcompany.eeos.comment.application.dto.*;
import com.blackcompany.eeos.common.presentation.respnose.ApiResponse;
import com.blackcompany.eeos.common.presentation.respnose.ApiResponseBody.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;

@Tag(name = "질문 및 댓글", description = "댓글 및 질문 관련 API")
public interface CommentApi {

@Operation(summary = "질문 및 코멘트 작성", description = "코멘트 및 질문을 생성합니다.")
ApiResponse<SuccessBody<CommandCommentResponse>> create(
@Parameter(hidden = true) Long memberId, @RequestBody CreateCommentRequest request);

@Operation(summary = "질문 및 코멘트 수정", description = "코멘트 및 질문을 수정합니다.")
ApiResponse<SuccessBody<CommandCommentResponse>> update(
@Parameter(hidden = true) Long memberId,
@PathVariable("commentId") Long commentId,
@RequestBody UpdateCommentRequest request);

@Operation(summary = "질문 및 코멘트 삭제", description = "코멘트 및 질문을 삭제합니다.")
ApiResponse<SuccessBody<Void>> delete(
@Parameter(hidden = true) Long memberId, @PathVariable("commentId") Long commentId);

@Operation(summary = "질문 및 코멘트 조회", description = "코멘트 및 질문을 조회합니다.")
ApiResponse<SuccessBody<QueryCommentsResponse>> getComments(
@Parameter(hidden = true) Long memberId,
@RequestParam("programId") Long programId,
@RequestParam("teamId") Long teamId);
}
Loading
Loading