From 705d136855bfd58e51cc40237e0e3bed9604f542 Mon Sep 17 00:00:00 2001 From: SeungJin LEE <105618997+SeungEEE@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:34:36 +0900 Subject: [PATCH 01/33] =?UTF-8?q?04/01=20=EC=8A=A4=ED=81=AC=EB=9F=BC=20?= =?UTF-8?q?=ED=9A=8C=EC=9D=98=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20240401.md" | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 "documents/\355\232\214\354\235\230\353\241\235/20240401.md" diff --git "a/documents/\355\232\214\354\235\230\353\241\235/20240401.md" "b/documents/\355\232\214\354\235\230\353\241\235/20240401.md" new file mode 100644 index 00000000..fc08f297 --- /dev/null +++ "b/documents/\355\232\214\354\235\230\353\241\235/20240401.md" @@ -0,0 +1,41 @@ +### 완료한 일 + +### Backend + +- 이준우 + - FCM 연동 +- 강재혁 + - Elevenlabs 목소리 추출 API WAS로 옮기기 (로직 구현) + +### Android + +- 영서 + - 코드 리팩토링 + +### iOS + +- 승진 + - 코드 리팩토링 + +### 할 일 + +### Backend + +- 이준우 + - 테스트 코드 작성 + - Email 제거 리팩토링 +- 강재혁 + - Elevenlabs 목소리 추출 API 연동을 위한 WebClient 적용 + +### Android + +- 영서 + - SceneDetailFragment 코드 리팩토링 + - s3 이미지 url로 만들기 + - 로그인 거치지 않고 AI 서버에 직접 밈 생성 api 연동 + +### iOS + +- 승진 + - iOS S3 연결 + - 로그인 연결 세팅 From c80f62a17f24cdc461be62f5a941c18d272805a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=9E=AC=ED=98=81?= Date: Fri, 5 Apr 2024 23:52:07 +0900 Subject: [PATCH 02/33] =?UTF-8?q?04/05=20=EC=8A=A4=ED=81=AC=EB=9F=BC=20?= =?UTF-8?q?=ED=9A=8C=EC=9D=98=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20240405.md" | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 "documents/\355\232\214\354\235\230\353\241\235/20240405.md" diff --git "a/documents/\355\232\214\354\235\230\353\241\235/20240405.md" "b/documents/\355\232\214\354\235\230\353\241\235/20240405.md" new file mode 100644 index 00000000..c5ab4781 --- /dev/null +++ "b/documents/\355\232\214\354\235\230\353\241\235/20240405.md" @@ -0,0 +1,56 @@ +# [스크럼] 회의록 + +status: Not started +날짜: 2024년 4월 5일 + +### 완료한 일 + +### Backend + +- 이준우 + - 테스트코드 작성중 +- 강재혁 + - Elevenlabs 목소리 추출 API WAS로 옮기기 (S3 연동 및 WebClient 적용) + +### Android + +- 영서 + - 밈 생성 데이터 넘어가는 구조 변경 (SceneDetailFragment) + +### iOS + +- 승진 + - iOS S3 연결 + +### 할 일 + +### Backend + +- 이준우 + - 테스트 코드 작성 + - JWT와 Member부분 코드 리팩토링 +- 강재혁 + - Elevenlabs 목소리 추출 API 연동 에러 해결 + +### Android + +- 영서 + - SceneDetailFragment 코드 리팩토링 + - s3 이미지 url로 만들기 + +### iOS + +- 승진 + - View / ViewController 분리 + - 로그인 연결 셋팅 + +## API 연동 해야할 것들 + +- 소셜로그인 + - 우리는 WAS에서 인증서버의 access_token을 받고 있음 + - 앱에서 SDK를 통해서 인증서버의 accees_token을 받음 + - [x] **access_token**을 그대로 서버에 보냄(지양해야 하는 방법이지만 다른 사람들도 쓰는 방법) + - [ ] 앱에서 **access_token**을 이용해서 **social_id** 와 **social_type**을 받아와서 서버로 전송하는 방법 + - 서버에서 보내준 5분짜리 JWT와 함께 보냄으로써, 보안에 신경을 씀 + - 백엔드에 바로 접속 → 소셜이랑 저쩌구 저쩌구 해서 그냥 서버 토큰 (서버랑 소셜간의 통신만 하고 있다. SDK는 프론트와 소셜이 통신 후 데이터를 서버로 보내주는 방식을 사용해야한다.) +- 밈 생성 \ No newline at end of file From 801b2087a9e9202b13602673a4b76aaaa9a04a9b Mon Sep 17 00:00:00 2001 From: JunRain Date: Sat, 6 Apr 2024 16:38:27 +0900 Subject: [PATCH 03/33] =?UTF-8?q?[#59]=20MemberService=EC=97=90=20?= =?UTF-8?q?=EC=A7=91=EC=A4=91=EB=90=98=EC=96=B4=20=EC=9E=88=EB=8A=94=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EC=B1=85=EC=9E=84=20=EB=B6=84?= =?UTF-8?q?=EC=82=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/controller/MemberApi.java | 38 ++++++++++--------- .../member/controller/MemberController.java | 27 ++++++++----- .../domain/member/service/MemberService.java | 22 ----------- .../resolver/LoginMemberArgumentResolver.java | 9 +++-- .../security/jwt/service/JwtService.java | 10 +++-- 5 files changed, 52 insertions(+), 54 deletions(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberApi.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberApi.java index 922de8ae..b1622736 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberApi.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberApi.java @@ -1,31 +1,35 @@ package com.example.memetory.domain.member.controller; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + import com.example.memetory.domain.member.dto.MemberSignUpRequest; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; +import jakarta.servlet.http.HttpServletResponse; @Tag(name = "Member") public interface MemberApi { - @Operation( - summary = "회원가입", - description = "첫 소셜로그인 후 추가 정보 기입", - security = {@SecurityRequirement(name = "access_token")} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "회원가입 성공!" - ) - }) - ResponseEntity register( - MemberSignUpRequest memberSignUpRequest, - @Parameter(hidden = true) String email - ); + @Operation( + summary = "회원가입", + description = "첫 소셜로그인 후 추가 정보 기입", + security = {@SecurityRequirement(name = "access_token")} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "회원가입 성공!" + ) + }) + ResponseEntity register( + HttpServletResponse response, + MemberSignUpRequest memberSignUpRequest, + @Parameter(hidden = true) String email + ); } diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java index a5798255..92a03b11 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java @@ -10,23 +10,32 @@ import com.example.memetory.domain.member.dto.MemberSignUpRequest; import com.example.memetory.domain.member.service.MemberService; import com.example.memetory.global.annotation.LoginMemberEmail; +import com.example.memetory.global.security.jwt.refresh.service.RefreshTokenService; +import com.example.memetory.global.security.jwt.service.JwtService; +import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @RestController @RequiredArgsConstructor public class MemberController implements MemberApi { + private final MemberService memberService; + private final JwtService jwtService; + private final RefreshTokenService refreshTokenService; - private final MemberService memberService; + @PostMapping("/sign-up") + @Override + public ResponseEntity register( + HttpServletResponse response, @RequestBody MemberSignUpRequest memberSignUpRequest, + @LoginMemberEmail String email) { + MemberServiceDto memberServiceDto = memberSignUpRequest.toServiceDto(email); - @PostMapping("/sign-up") - @Override - public ResponseEntity register(@RequestBody MemberSignUpRequest memberSignUpRequest, - @LoginMemberEmail String email) { - MemberServiceDto memberServiceDto = memberSignUpRequest.toServiceDto(email); + memberService.register(memberServiceDto); - memberService.register(memberServiceDto); + String refreshToken = jwtService.createRefreshToken(); + jwtService.setRefreshTokenHeader(response, refreshToken); + refreshTokenService.updateToken(email, refreshToken); - return ResponseEntity.status(HttpStatus.OK).build(); - } + return ResponseEntity.status(HttpStatus.OK).build(); + } } diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/service/MemberService.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/service/MemberService.java index cf938a35..83642344 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/service/MemberService.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/service/MemberService.java @@ -4,17 +4,10 @@ import org.springframework.transaction.annotation.Transactional; import com.example.memetory.domain.member.dto.MemberServiceDto; -import com.example.memetory.domain.member.dto.MemberSignUpRequest; import com.example.memetory.domain.member.entity.Member; import com.example.memetory.domain.member.exception.NotFoundMemberException; import com.example.memetory.domain.member.repository.MemberRepository; -import com.example.memetory.global.security.jwt.exception.NotFoundEmailException; -import com.example.memetory.global.security.jwt.exception.NotFoundTokenException; -import com.example.memetory.global.security.jwt.refresh.service.RefreshTokenService; -import com.example.memetory.global.security.jwt.service.JwtService; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -22,28 +15,13 @@ @RequiredArgsConstructor @Slf4j public class MemberService { - private final MemberRepository memberRepository; - private final JwtService jwtService; - private final RefreshTokenService refreshTokenService; - private final HttpServletRequest request; - private final HttpServletResponse response; @Transactional public void register(MemberServiceDto memberServiceDto) { Member member = findByEmail(memberServiceDto.getEmail()); member.register(memberServiceDto); - - String refreshToken = jwtService.createRefreshToken(); - - jwtService.setRefreshTokenHeader(response, refreshToken); - refreshTokenService.updateToken(member.getEmail(), refreshToken); - } - - public String getMemberByEmail() { - String accessToken = jwtService.extractAccessToken(request).orElseThrow(NotFoundTokenException::new); - return jwtService.extractEmail(accessToken).orElseThrow(NotFoundEmailException::new); } @Transactional(readOnly = true) diff --git a/backend/memetory/src/main/java/com/example/memetory/global/resolver/LoginMemberArgumentResolver.java b/backend/memetory/src/main/java/com/example/memetory/global/resolver/LoginMemberArgumentResolver.java index 9e43b3e1..f96466f9 100644 --- a/backend/memetory/src/main/java/com/example/memetory/global/resolver/LoginMemberArgumentResolver.java +++ b/backend/memetory/src/main/java/com/example/memetory/global/resolver/LoginMemberArgumentResolver.java @@ -7,9 +7,10 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; -import com.example.memetory.domain.member.service.MemberService; import com.example.memetory.global.annotation.LoginMemberEmail; +import com.example.memetory.global.security.jwt.service.JwtService; +import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -18,9 +19,10 @@ @Slf4j public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver { - private final MemberService memberService; + private final JwtService jwtService; @Override + public boolean supportsParameter(MethodParameter methodParameter) { return methodParameter.hasParameterAnnotation(LoginMemberEmail.class); } @@ -28,6 +30,7 @@ public boolean supportsParameter(MethodParameter methodParameter) { @Override public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) { - return memberService.getMemberByEmail(); + HttpServletRequest request = (HttpServletRequest)nativeWebRequest.getNativeRequest(); + return jwtService.getEmail(request); } } \ No newline at end of file diff --git a/backend/memetory/src/main/java/com/example/memetory/global/security/jwt/service/JwtService.java b/backend/memetory/src/main/java/com/example/memetory/global/security/jwt/service/JwtService.java index 09ef4585..35ed9948 100644 --- a/backend/memetory/src/main/java/com/example/memetory/global/security/jwt/service/JwtService.java +++ b/backend/memetory/src/main/java/com/example/memetory/global/security/jwt/service/JwtService.java @@ -3,8 +3,8 @@ import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.example.memetory.domain.member.repository.MemberRepository; -import com.example.memetory.global.security.jwt.refresh.domain.RefreshToken; -import com.example.memetory.global.security.jwt.refresh.repository.RefreshTokenRepository; +import com.example.memetory.global.security.jwt.exception.NotFoundEmailException; +import com.example.memetory.global.security.jwt.exception.NotFoundTokenException; import com.example.memetory.global.security.jwt.refresh.service.RefreshTokenService; import jakarta.servlet.http.HttpServletRequest; @@ -15,7 +15,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import java.util.Date; import java.util.Optional; @@ -112,6 +111,11 @@ public Optional extractEmail(String accessToken) { } } + public String getEmail(HttpServletRequest request) { + String accessToken = this.extractAccessToken(request).orElseThrow(NotFoundTokenException::new); + return this.extractEmail(accessToken).orElseThrow(NotFoundEmailException::new); + } + // 헤더에 accessToken 설정 public void setAccessTokenHeader(HttpServletResponse response, String accessToken) { response.setHeader(accessHeader,BEARER + accessToken); From 20d82722cb93454c8876ae87696ff1c98544a920 Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 7 Apr 2024 20:18:23 +0900 Subject: [PATCH 04/33] =?UTF-8?q?[#59]=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=99=80=20=EA=B4=80=EB=A0=A8=EB=90=9C=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/memetory/build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/memetory/build.gradle b/backend/memetory/build.gradle index 7eae8f68..93d529c4 100644 --- a/backend/memetory/build.gradle +++ b/backend/memetory/build.gradle @@ -40,6 +40,11 @@ dependencies { // Swagger 적용 implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' + + // test에 lombok 추가 + testCompileOnly 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.security:spring-security-test' } tasks.named('test') { From acc241c8872d43f25041ffa7ac64b457ea6e8f8e Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 7 Apr 2024 20:19:25 +0900 Subject: [PATCH 05/33] =?UTF-8?q?[#59]=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=9A=A9=20Member=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../memetory/domain/member/MemberFixture.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 backend/memetory/src/test/java/com/example/memetory/domain/member/MemberFixture.java diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/member/MemberFixture.java b/backend/memetory/src/test/java/com/example/memetory/domain/member/MemberFixture.java new file mode 100644 index 00000000..d4fb3d18 --- /dev/null +++ b/backend/memetory/src/test/java/com/example/memetory/domain/member/MemberFixture.java @@ -0,0 +1,35 @@ +package com.example.memetory.domain.member; + +import com.example.memetory.domain.member.dto.MemberServiceDto; +import com.example.memetory.domain.member.entity.Member; +import com.example.memetory.domain.member.entity.Role; +import com.example.memetory.domain.member.entity.SocialType; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class MemberFixture { + public final static Member MEMBER = Member.builder() + .email("junrain@ourservice.com") + .role(Role.USER) + .imageUrl("imageUrl") + .nickname("이준우") + .socialType(SocialType.GOOGLE) + .socialId("-1") + .build(); + + public final static Member GUEST_MEMBER = Member.builder() + .email("junrain@ourservice.com") + .role(Role.GUEST) + .imageUrl("imageUrl") + .nickname("이준우") + .socialType(SocialType.GOOGLE) + .socialId("-1") + .build(); + + public final static MemberServiceDto MEMBER_SERVICE_DTO = MemberServiceDto.builder() + .imageUrl("imageUrl") + .nickname("junRain") + .email("junrain@ourservice.com") + .build(); +} From 891fa0900eb863300d466dd066c8a80f5058c1a3 Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 7 Apr 2024 20:19:50 +0900 Subject: [PATCH 06/33] =?UTF-8?q?[#59]=20MemberRepository=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/MemberRepositoryTest.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 backend/memetory/src/test/java/com/example/memetory/domain/member/repository/MemberRepositoryTest.java diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/member/repository/MemberRepositoryTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/member/repository/MemberRepositoryTest.java new file mode 100644 index 00000000..26b1b2b4 --- /dev/null +++ b/backend/memetory/src/test/java/com/example/memetory/domain/member/repository/MemberRepositoryTest.java @@ -0,0 +1,52 @@ +package com.example.memetory.domain.member.repository; + +import static com.example.memetory.domain.member.MemberFixture.*; +import static org.assertj.core.api.AssertionsForClassTypes.*; + +import java.util.Optional; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +import com.example.memetory.domain.member.entity.Member; +import com.example.memetory.global.config.JpaAuditingConfig; + +@DataJpaTest +@DisplayName("member 레포지토리 테스트의 ") +@Import(JpaAuditingConfig.class) +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +public class MemberRepositoryTest { + @Autowired + private MemberRepository memberRepository; + + @Test + @DisplayName("이메일로 멤버 찾기") + public void 이메일로_Member_찾기() { + // given -> 멤버 저장하기 + Member savedMember = memberRepository.save(MEMBER); + + // when 멤버 찾기 + Optional findMember = memberRepository.findByEmail(savedMember.getEmail()); + + // then 확인하기 + assertThat(savedMember).isEqualTo(findMember.get()); + } + + @Test + @DisplayName("SocialType과 SocialId로 멤버 찾기") + public void SocialType과_SocialId로_Member_찾기() { + // given -> 멤버 저장하기 + Member savedMember = memberRepository.save(MEMBER); + + // when 멤버 찾기 + Optional findMember = memberRepository + .findBySocialTypeAndSocialId(savedMember.getSocialType(), savedMember.getSocialId()); + + // then 확인하기 + assertThat(savedMember).isEqualTo(findMember.get()); + } +} From d3687593935fbca77ab9d0a6dc306dff07d37847 Mon Sep 17 00:00:00 2001 From: JunRain Date: Sun, 7 Apr 2024 20:20:10 +0900 Subject: [PATCH 07/33] =?UTF-8?q?[#59]=20MemberService=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/MemberServiceTest.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 backend/memetory/src/test/java/com/example/memetory/domain/member/service/MemberServiceTest.java diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/member/service/MemberServiceTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/member/service/MemberServiceTest.java new file mode 100644 index 00000000..3a10ade7 --- /dev/null +++ b/backend/memetory/src/test/java/com/example/memetory/domain/member/service/MemberServiceTest.java @@ -0,0 +1,71 @@ +package com.example.memetory.domain.member.service; + +import static com.example.memetory.domain.member.MemberFixture.*; +import static org.assertj.core.api.Assertions.*; + +import java.util.Optional; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import com.example.memetory.domain.member.entity.Member; +import com.example.memetory.domain.member.entity.Role; +import com.example.memetory.domain.member.repository.MemberRepository; +import com.example.memetory.global.security.jwt.service.JwtService; + +@DisplayName("memberService의 ") +@Transactional +@SpringBootTest +public class MemberServiceTest { + @Autowired + private MemberService memberService; + @Autowired + private JwtService jwtService; + @Autowired + private MemberRepository memberRepository; + + @Test + @DisplayName("회원가입이 잘 진행 되는가?") + void 회원가입() { + // given 멤버 저장 + Member savedMember = memberRepository.save(GUEST_MEMBER); + + // when 실행 + memberService.register(MEMBER_SERVICE_DTO); + + // then 확인 ROLE과 닉네임이 잘 변경 됐는지 확인 + Optional updatedMember = memberRepository.findByEmail(GUEST_MEMBER.getEmail()); + assertThat(MEMBER_SERVICE_DTO.getNickname()).isEqualTo(updatedMember.get().getNickname()); + assertThat(Role.USER).isEqualTo(updatedMember.get().getRole()); + } + + @Test + @DisplayName("이메일을 통해서 멤버 가져오기") + void 이메일을_통해서_Member_불러오기() { + // given 멤버 저장 + Member savedMember = memberRepository.save(MEMBER); + + // when 실행 + Member findMember = memberService.findByEmail(savedMember.getEmail()); + + // then + assertThat(findMember.getId()).isEqualTo(savedMember.getId()); + + } + + @Test + @DisplayName("ID를 통해서 멤버 가져오기") + void ID를_통해서_Member_불러오기() { + // given 멤버 저장 + Member savedMember = memberRepository.save(MEMBER); + + // when 실행 + Member findMember = memberService.findById(savedMember.getId()); + + // then + assertThat(findMember.getId()).isEqualTo(savedMember.getId()); + } +} From b278eb92309a799f88ba444a9da2bd0407ded569 Mon Sep 17 00:00:00 2001 From: JunRain Date: Mon, 8 Apr 2024 00:06:43 +0900 Subject: [PATCH 08/33] =?UTF-8?q?[#59]=20MemberController=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/dto/MemberSignUpRequest.java | 2 + .../handler/OAuth2LoginSuccessHandler.java | 2 +- .../memetory/config/SecurityTestConfig.java | 52 +++++++++++ .../controller/MemberControllerTest.java | 90 +++++++++++++++++++ 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 backend/memetory/src/test/java/com/example/memetory/config/SecurityTestConfig.java create mode 100644 backend/memetory/src/test/java/com/example/memetory/domain/member/controller/MemberControllerTest.java diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/dto/MemberSignUpRequest.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/dto/MemberSignUpRequest.java index 946dda25..647c892d 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/dto/MemberSignUpRequest.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/dto/MemberSignUpRequest.java @@ -2,9 +2,11 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; +@AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PRIVATE) @Getter @Schema(description = "회원 가입 포맷") diff --git a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/handler/OAuth2LoginSuccessHandler.java b/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/handler/OAuth2LoginSuccessHandler.java index 424ae231..d3d95781 100644 --- a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/handler/OAuth2LoginSuccessHandler.java +++ b/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/handler/OAuth2LoginSuccessHandler.java @@ -36,7 +36,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo if (oAuth2User.getRole() == Role.GUEST) { String accessToken = jwtService.createAccessToken(oAuth2User.getEmail()); response.addHeader(jwtService.getAccessHeader(), "Bearer " + accessToken); - // response.sendRedirect("oauth2/sign-up"); // 프론트의 회원가입 추가 정보 입력 폼으로 리다이렉트 + // response.sendRedirect("/sign-up"); // 프론트의 회원가입 추가 정보 입력 폼으로 리다이렉트 jwtService.setAccessTokenHeader(response, accessToken); diff --git a/backend/memetory/src/test/java/com/example/memetory/config/SecurityTestConfig.java b/backend/memetory/src/test/java/com/example/memetory/config/SecurityTestConfig.java new file mode 100644 index 00000000..abc1e18a --- /dev/null +++ b/backend/memetory/src/test/java/com/example/memetory/config/SecurityTestConfig.java @@ -0,0 +1,52 @@ +package com.example.memetory.config; + +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.logout.LogoutFilter; + +import com.example.memetory.domain.member.repository.MemberRepository; +import com.example.memetory.global.security.jwt.filter.JwtAuthenticationProcessingFilter; +import com.example.memetory.global.security.jwt.refresh.service.RefreshTokenService; +import com.example.memetory.global.security.jwt.service.JwtService; + +import lombok.RequiredArgsConstructor; + +@TestConfiguration +@RequiredArgsConstructor +public class SecurityTestConfig { + private final JwtService jwtService; + private final MemberRepository memberRepository; + private final RefreshTokenService refreshTokenService; + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.formLogin(AbstractHttpConfigurer::disable) // 기본 제공하는 로그인 Form 사용 X + .httpBasic(AbstractHttpConfigurer::disable) // Bearer방식이기 때문에 httpBasic 사용 X + .csrf(AbstractHttpConfigurer::disable) //csrf 보안 사용 X + .sessionManagement((sessionManagement) -> // 세션은 사용하지 않기 때문에 stateless 설정 + sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(authorizeRequests // 개발을 진행하기 위해 일단 모든 url 허용 + -> authorizeRequests.anyRequest().permitAll()); + + http.addFilterAfter(jwtAuthenticationProcessingFilter(), LogoutFilter.class); + + return http.build(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + } + + @Bean + public JwtAuthenticationProcessingFilter jwtAuthenticationProcessingFilter() { + return new JwtAuthenticationProcessingFilter(jwtService, memberRepository, refreshTokenService); + } +} diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/member/controller/MemberControllerTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/member/controller/MemberControllerTest.java new file mode 100644 index 00000000..762a13ec --- /dev/null +++ b/backend/memetory/src/test/java/com/example/memetory/domain/member/controller/MemberControllerTest.java @@ -0,0 +1,90 @@ +package com.example.memetory.domain.member.controller; + +import static com.example.memetory.domain.member.MemberFixture.*; +import static org.mockito.BDDMockito.*; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.Date; +import java.util.Optional; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.example.memetory.config.SecurityTestConfig; +import com.example.memetory.domain.member.dto.MemberSignUpRequest; +import com.example.memetory.domain.member.repository.MemberRepository; +import com.example.memetory.domain.member.service.MemberService; +import com.example.memetory.global.security.jwt.refresh.service.RefreshTokenService; +import com.example.memetory.global.security.jwt.service.JwtService; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +@DisplayName("Member 컨트롤러 테스트의 ") +@WebMvcTest(MemberController.class) +@Import(SecurityTestConfig.class) +public class MemberControllerTest { + + @Autowired + MockMvc mockMvc; + @Autowired + private ObjectMapper objectMapper; + + @MockBean + private JwtService jwtService; + @MockBean + private MemberService memberService; + @MockBean + private RefreshTokenService refreshTokenService; + @MockBean + private MemberRepository memberRepository; + + @Value("${jwt.secretKey}") + private String secretKey; + private String authorization_jwt; + + @BeforeEach + void setUp() { + Date now = new Date(); + authorization_jwt = JWT.create() + .withSubject("AccessToken") + .withExpiresAt(new Date(now.getTime() + 180000)) + .withClaim("email", GUEST_MEMBER.getId()) + .sign(Algorithm.HMAC512(secretKey)); + } + + // 잘 된 테스트인지는 아직 의문 + @Test + @DisplayName("회원가입이 완료되었는가") + void 회원가입() throws Exception { + // given -> 결과에 대한 객체, Member 객체 저장할 필요 존재 + given(memberRepository.findByEmail(GUEST_MEMBER.getEmail())).willReturn(Optional.ofNullable(GUEST_MEMBER)); + + // when + final ResultActions perform = mockMvc.perform( + post("/sign-up") + .contentType(MediaType.APPLICATION_JSON) + .content(toRequestBody(new MemberSignUpRequest("Memetory"))) + .header("Authorization", "Bearer " + authorization_jwt) + ); + + // then + perform.andExpect(status().isOk()); + } + + protected String toRequestBody(Object value) throws JsonProcessingException { + return objectMapper.writeValueAsString(value); + } +} From cdfb40ee113408a4875c54995bb6a25f15bbc3ce Mon Sep 17 00:00:00 2001 From: SeungJin LEE <105618997+SeungEEE@users.noreply.github.com> Date: Mon, 8 Apr 2024 22:37:17 +0900 Subject: [PATCH 09/33] =?UTF-8?q?04/08=20=EC=8A=A4=ED=81=AC=EB=9F=BC=20?= =?UTF-8?q?=ED=9A=8C=EC=9D=98=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20240408.md" | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 "documents/\355\232\214\354\235\230\353\241\235/20240408.md" diff --git "a/documents/\355\232\214\354\235\230\353\241\235/20240408.md" "b/documents/\355\232\214\354\235\230\353\241\235/20240408.md" new file mode 100644 index 00000000..0dd78422 --- /dev/null +++ "b/documents/\355\232\214\354\235\230\353\241\235/20240408.md" @@ -0,0 +1,42 @@ +### 완료한 일 + +### Backend + +- 이준우 + - Member 테스트 코드 작성 + - 인증서버 access_token을 통해 우리 서버에 User 정보를 받아오는 부분 조사 +- 강재혁 + - Elevenlabs 목소리 추출 API WAS로 옮기기 (S3 연동 및 WebClient 적용) + - Elevenlabs 목소리 추출 API 연동 에러 해결 + +### Android + +- 영서 + - ViewModel 추가 중 + +### iOS + +- 승진 + - iOS S3 연결 + +### 할 일 + +### Backend + +- 이준우 + - JWT와 Member부분 코드 리팩토링 + - 로그인부분 인증서버의 Access_token을 받는 방식으로 리팩토링 +- 강재혁 + - Elevenlabs 목소리 추출 API 파일 다운로드 하지 않고 S3URL로 바로 넘기기 + +### Android + +- 영서 + - SceneDetailFragment 코드 리팩토링 + - s3 이미지 url로 만들기 + - 로그인 코드 작성 + +### iOS + +- 승진 + - 소셜 로그인 구현 From 0965403fc62b259aacc941f432f6cc16c916bc61 Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 9 Apr 2024 23:47:03 +0900 Subject: [PATCH 10/33] =?UTF-8?q?[#62]=20Member=EC=97=94=ED=8B=B0=ED=8B=B0?= =?UTF-8?q?=EC=97=90=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=9D=B4=EB=A6=84?= =?UTF-8?q?=EC=9D=84=20=EC=A0=80=EC=9E=A5=ED=95=98=EB=8A=94=20name=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/memetory/domain/member/entity/Member.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/Member.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/Member.java index 39d7e6cc..02f0da48 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/Member.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/Member.java @@ -1,7 +1,6 @@ package com.example.memetory.domain.member.entity; import com.example.memetory.domain.member.dto.MemberServiceDto; -import com.example.memetory.domain.member.dto.MemberSignUpRequest; import com.example.memetory.global.entity.BaseEntity; import jakarta.persistence.Column; @@ -30,6 +29,7 @@ public class Member extends BaseEntity { private String email; private String nickname; + private String name; private String imageUrl; @Enumerated(EnumType.STRING) @@ -41,17 +41,19 @@ public class Member extends BaseEntity { private String socialId; @Builder - public Member(String email, String nickname, String imageUrl, Role role, SocialType socialType, String socialId) { + public Member(String email, String nickname, String name, String imageUrl, Role role, SocialType socialType, + String socialId) { this.email = email; this.nickname = nickname; + this.name = name; this.imageUrl = imageUrl; this.role = role; this.socialType = socialType; this.socialId = socialId; } - public void register(MemberServiceDto memberServiceDto) { + // Todo 닉네임이랑, 이미지 변경할 수 있게 하기 + public void update(MemberServiceDto memberServiceDto) { this.nickname = memberServiceDto.getNickname(); - this.role = Role.USER; } } From bd290f877d2d889c2b2d91e6cfafaf79293f4ced Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 9 Apr 2024 23:47:31 +0900 Subject: [PATCH 11/33] =?UTF-8?q?[#62]=20SocialType=EC=97=90=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=EA=B0=92=EC=9C=BC=EB=A1=9C=20=EA=B3=B5=EA=B8=89?= =?UTF-8?q?=EC=9E=90=20Url=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20kakao=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/entity/SocialType.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/SocialType.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/SocialType.java index 04cbaf0b..db8c0124 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/SocialType.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/SocialType.java @@ -1,5 +1,19 @@ package com.example.memetory.domain.member.entity; +import com.fasterxml.jackson.annotation.JsonCreator; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter public enum SocialType { - KAKAO, GOOGLE + GOOGLE("https://www.googleapis.com/oauth2/v2/userinfo"); + + private final String providerUrl; + + @JsonCreator + public static SocialType from(String s) { + return SocialType.valueOf(s.toUpperCase()); + } } From af9c65e2a1d1d993949ce47ee88d953e46841c4b Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 9 Apr 2024 23:47:49 +0900 Subject: [PATCH 12/33] =?UTF-8?q?[#62]=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EB=A1=9C=EC=A7=81=20=EC=A0=9C=EA=B1=B0=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20GUEST=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/memetory/domain/member/entity/Role.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/Role.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/Role.java index 97a4a0a8..c06002b9 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/Role.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/Role.java @@ -6,7 +6,7 @@ @Getter @RequiredArgsConstructor public enum Role { - GUEST("ROLE_GUEST"), USER("ROLE_USER"); + USER("ROLE_USER"); private final String key; } From d4d2b30f398d4149b7d5c15b544fdaaecf009961 Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 9 Apr 2024 23:49:26 +0900 Subject: [PATCH 13/33] =?UTF-8?q?[#62]=20=EC=9D=B8=EC=A6=9D=EC=84=9C?= =?UTF-8?q?=EB=B2=84=EC=9D=98=20=ED=83=80=EC=9E=85=EA=B3=BC=20token?= =?UTF-8?q?=EC=9D=84=20=EB=B0=9B=EC=95=84=EC=98=A4=EB=8A=94=20DTO=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../memetory/domain/auth/dto/LoginRequest.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 backend/memetory/src/main/java/com/example/memetory/domain/auth/dto/LoginRequest.java diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/auth/dto/LoginRequest.java b/backend/memetory/src/main/java/com/example/memetory/domain/auth/dto/LoginRequest.java new file mode 100644 index 00000000..e5b0ca3e --- /dev/null +++ b/backend/memetory/src/main/java/com/example/memetory/domain/auth/dto/LoginRequest.java @@ -0,0 +1,18 @@ +package com.example.memetory.domain.auth.dto; + +import com.example.memetory.domain.member.entity.SocialType; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@Schema(description = "로그인 포맷") +public class LoginRequest { + @Schema(description = "인증서버에서 받아온 access token을 입력") + private String token; + @Schema(description = "인증서버를 소문자로 작성, 현재는 google만 가능") + private SocialType socialType; +} From 9adf087d760363dc079535252226b2fa4b20bb40 Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 9 Apr 2024 23:50:17 +0900 Subject: [PATCH 14/33] =?UTF-8?q?[#62]=20=EC=9D=B8=EC=A6=9D=EC=84=9C?= =?UTF-8?q?=EB=B2=84=EB=A1=9C=EB=B6=80=ED=84=B0=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EC=9E=90=20=EC=A0=95=EB=B3=B4=EB=A5=BC=20=EB=B0=9B=EC=95=84?= =?UTF-8?q?=EC=98=A4=EB=8A=94=20Service=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/service/OAuth2ProviderService.java | 48 +++++++++++++++++++ .../auth}/userInfo/GoogleOAuth2UserInfo.java | 9 ++-- .../auth}/userInfo/OAuth2UserInfo.java | 4 +- 3 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 backend/memetory/src/main/java/com/example/memetory/domain/auth/service/OAuth2ProviderService.java rename backend/memetory/src/main/java/com/example/memetory/{global/security/oauth => domain/auth}/userInfo/GoogleOAuth2UserInfo.java (69%) rename backend/memetory/src/main/java/com/example/memetory/{global/security/oauth => domain/auth}/userInfo/OAuth2UserInfo.java (77%) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/auth/service/OAuth2ProviderService.java b/backend/memetory/src/main/java/com/example/memetory/domain/auth/service/OAuth2ProviderService.java new file mode 100644 index 00000000..b2003640 --- /dev/null +++ b/backend/memetory/src/main/java/com/example/memetory/domain/auth/service/OAuth2ProviderService.java @@ -0,0 +1,48 @@ +package com.example.memetory.domain.auth.service; + +import static com.example.memetory.domain.member.entity.SocialType.*; + +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClient; + +import com.example.memetory.domain.auth.dto.LoginRequest; +import com.example.memetory.domain.auth.userInfo.GoogleOAuth2UserInfo; +import com.example.memetory.domain.auth.userInfo.OAuth2UserInfo; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class OAuth2ProviderService { + + private static final Logger log = LoggerFactory.getLogger(OAuth2ProviderService.class); + + public OAuth2UserInfo getUserInfo(LoginRequest request) { + return switch (request.getSocialType()) { + case GOOGLE -> getGoogleUserInfo(request); + }; + } + + private OAuth2UserInfo getGoogleUserInfo(LoginRequest request) { + Map attributes = WebClient.create(GOOGLE.getProviderUrl()) + .get() + .headers(httpHeaders -> { + httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + httpHeaders.setBearerAuth(request.getToken()); + }) + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .bodyToMono(Map.class) + .log() + .block(); + + return new GoogleOAuth2UserInfo(attributes); + } +} + diff --git a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/userInfo/GoogleOAuth2UserInfo.java b/backend/memetory/src/main/java/com/example/memetory/domain/auth/userInfo/GoogleOAuth2UserInfo.java similarity index 69% rename from backend/memetory/src/main/java/com/example/memetory/global/security/oauth/userInfo/GoogleOAuth2UserInfo.java rename to backend/memetory/src/main/java/com/example/memetory/domain/auth/userInfo/GoogleOAuth2UserInfo.java index 07db673a..864f0b30 100644 --- a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/userInfo/GoogleOAuth2UserInfo.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/auth/userInfo/GoogleOAuth2UserInfo.java @@ -1,7 +1,10 @@ -package com.example.memetory.global.security.oauth.userInfo; +package com.example.memetory.domain.auth.userInfo; import java.util.Map; +import lombok.extern.slf4j.Slf4j; + +@Slf4j public class GoogleOAuth2UserInfo extends OAuth2UserInfo { public GoogleOAuth2UserInfo(Map attributes) { @@ -10,11 +13,11 @@ public GoogleOAuth2UserInfo(Map attributes) { @Override public String getId() { - return (String)attributes.get("sub"); + return (String)attributes.get("id"); } @Override - public String getNickname() { + public String getName() { return (String)attributes.get("name"); } diff --git a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/userInfo/OAuth2UserInfo.java b/backend/memetory/src/main/java/com/example/memetory/domain/auth/userInfo/OAuth2UserInfo.java similarity index 77% rename from backend/memetory/src/main/java/com/example/memetory/global/security/oauth/userInfo/OAuth2UserInfo.java rename to backend/memetory/src/main/java/com/example/memetory/domain/auth/userInfo/OAuth2UserInfo.java index 058238b8..6569bd4b 100644 --- a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/userInfo/OAuth2UserInfo.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/auth/userInfo/OAuth2UserInfo.java @@ -1,4 +1,4 @@ -package com.example.memetory.global.security.oauth.userInfo; +package com.example.memetory.domain.auth.userInfo; import java.util.Map; @@ -12,7 +12,7 @@ public OAuth2UserInfo(Map attributes) { public abstract String getId(); //소셜 식별 값 : 구글 - "sub", 카카오 - "id", 네이버 - "id" - public abstract String getNickname(); + public abstract String getName(); public abstract String getImageUrl(); } From d7dfb599d77cb46eb9bc913048617e3e3c9e5ce1 Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 9 Apr 2024 23:51:33 +0900 Subject: [PATCH 15/33] =?UTF-8?q?[#62]=20JwtService=EC=9D=98=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/jwt/service/JwtService.java | 50 ++++++++----------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/global/security/jwt/service/JwtService.java b/backend/memetory/src/main/java/com/example/memetory/global/security/jwt/service/JwtService.java index 35ed9948..72695482 100644 --- a/backend/memetory/src/main/java/com/example/memetory/global/security/jwt/service/JwtService.java +++ b/backend/memetory/src/main/java/com/example/memetory/global/security/jwt/service/JwtService.java @@ -1,5 +1,11 @@ package com.example.memetory.global.security.jwt.service; +import java.util.Date; +import java.util.Optional; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.example.memetory.domain.member.repository.MemberRepository; @@ -13,12 +19,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import java.util.Date; -import java.util.Optional; - @Service @RequiredArgsConstructor @Getter @@ -48,18 +48,26 @@ public class JwtService { @Value("${jwt.refresh.header}") private String refreshHeader; - public String createAccessToken(String email) { + // 함께 보낼때는 둘 다 create + public void sendAccessAndRefreshToken(HttpServletResponse response, String email) { + setTokenHeader(response, accessHeader, createAccessToken(email)); + + String refreshToken = createRefreshToken(); + setTokenHeader(response, refreshHeader, refreshToken); + refreshTokenService.updateToken(email, refreshToken); + log.info("Access Token, Refresh Token 헤더 설정 완료"); + } + + private String createAccessToken(String email) { Date now = new Date(); return JWT.create() // JWT 토큰을 생성하는 빌더 반환 .withSubject(ACCESS_TOKEN_SUBJECT) // JWT의 Subject 지정 -> AccessToken이므로 AccessToken .withExpiresAt(new Date(now.getTime() + accessTokenExpirationPeriod)) // 토큰 만료 시간 설정 - - //추가할 경우 .withClaim(클래임 이름, 클래임 값)으로 설정 .withClaim(EMAIL_CLAIM, email) .sign(Algorithm.HMAC512(secretKey)); } - public String createRefreshToken() { + private String createRefreshToken() { Date now = new Date(); return JWT.create() .withSubject(REFRESH_TOKEN_SUBJECT) @@ -67,14 +75,6 @@ public String createRefreshToken() { .sign(Algorithm.HMAC512(secretKey)); } - public void sendAccessAndRefreshToken(HttpServletResponse response, String accessToken, String refreshToken) { - response.setStatus(HttpServletResponse.SC_OK); - - setAccessTokenHeader(response, accessToken); - setRefreshTokenHeader(response, refreshToken); - log.info("Access Token, Refresh Token 헤더 설정 완료"); - } - // 헤더에서 RefreshToken 추출 public Optional extractRefreshToken(HttpServletRequest request) { return Optional.ofNullable(request.getHeader(refreshHeader)) @@ -116,18 +116,8 @@ public String getEmail(HttpServletRequest request) { return this.extractEmail(accessToken).orElseThrow(NotFoundEmailException::new); } - // 헤더에 accessToken 설정 - public void setAccessTokenHeader(HttpServletResponse response, String accessToken) { - response.setHeader(accessHeader,BEARER + accessToken); - } - - // 헤더에 refreshToken 설정 - public void setRefreshTokenHeader(HttpServletResponse response, String refreshToken) { - response.setHeader(refreshHeader,BEARER + refreshToken); - } - - public void updateRefreshToken(String email, String token) { - refreshTokenService.updateToken(email, token); + private void setTokenHeader(HttpServletResponse response, String headerName, String token) { + response.setHeader(headerName, BEARER + token); } public boolean isTokenValid(String token) { From a1f19f6a1c84247af1b1402d88384807df1ec84d Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 9 Apr 2024 23:53:06 +0900 Subject: [PATCH 16/33] =?UTF-8?q?[#62]=20JwtService=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20Jwt?= =?UTF-8?q?Filter=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../JwtAuthenticationProcessingFilter.java | 28 ++++--------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/global/security/jwt/filter/JwtAuthenticationProcessingFilter.java b/backend/memetory/src/main/java/com/example/memetory/global/security/jwt/filter/JwtAuthenticationProcessingFilter.java index f9452126..6e514048 100644 --- a/backend/memetory/src/main/java/com/example/memetory/global/security/jwt/filter/JwtAuthenticationProcessingFilter.java +++ b/backend/memetory/src/main/java/com/example/memetory/global/security/jwt/filter/JwtAuthenticationProcessingFilter.java @@ -65,9 +65,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse // -> RefreshToken이 없거나 유효하지 않다면(DB에 저장된 RefreshToken과 다르다면) null을 반환 // 사용자의 요청 헤더에 RefreshToken이 있는 경우는, AccessToken이 만료되어 요청한 경우밖에 없다. // 따라서, 위의 경우를 제외하면 추출한 refreshToken은 모두 null - String refreshToken = jwtService.extractRefreshToken(request) - .filter(jwtService::isTokenValid) - .orElse(null); + String refreshToken = jwtService.extractRefreshToken(request).filter(jwtService::isTokenValid).orElse(null); // 리프레시 토큰이 요청 헤더에 존재했다면, 사용자가 AccessToken이 만료되어서 // RefreshToken까지 보낸 것이므로 리프레시 토큰이 DB의 리프레시 토큰과 일치하는지 판단 후, @@ -94,21 +92,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse */ public void checkRefreshTokenAndReIssueAccessToken(HttpServletResponse response, String refreshToken) { RefreshToken refresh = refreshTokenService.findByToken(refreshToken); - String reIssuedRefreshToken = reIssueRefreshToken(refresh.getEmail()); - jwtService.sendAccessAndRefreshToken(response, jwtService.createAccessToken(refresh.getEmail()), - reIssuedRefreshToken); - } - - /** - * [리프레시 토큰 재발급 & DB에 리프레시 토큰 업데이트 메소드] - * jwtService.createRefreshToken()으로 리프레시 토큰 재발급 후 - * DB에 재발급한 리프레시 토큰 업데이트 후 Flush - */ - private String reIssueRefreshToken(String email) { - String reIssuedRefreshToken = jwtService.createRefreshToken(); - // redis 에 업데이트해주는 코드 - refreshTokenService.updateToken(email, reIssuedRefreshToken); - return reIssuedRefreshToken; + jwtService.sendAccessAndRefreshToken(response, refresh.getEmail()); } /** @@ -125,8 +109,7 @@ public void checkAccessTokenAndAuthentication(HttpServletRequest request, HttpSe jwtService.extractAccessToken(request) .filter(jwtService::isTokenValid) .ifPresent(accessToken -> jwtService.extractEmail(accessToken) - .ifPresent(email -> memberRepository.findByEmail(email) - .ifPresent(this::saveAuthentication))); + .ifPresent(email -> memberRepository.findByEmail(email).ifPresent(this::saveAuthentication))); filterChain.doFilter(request, response); } @@ -156,9 +139,8 @@ public void saveAuthentication(Member myMember) { .roles(myMember.getRole().name()) .build(); - Authentication authentication = - new UsernamePasswordAuthenticationToken(userDetailsUser, null, - authoritiesMapper.mapAuthorities(userDetailsUser.getAuthorities())); + Authentication authentication = new UsernamePasswordAuthenticationToken(userDetailsUser, null, + authoritiesMapper.mapAuthorities(userDetailsUser.getAuthorities())); SecurityContextHolder.getContext().setAuthentication(authentication); } From ebfa101ea2acb76175dd23ec7bc32e834246f339 Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 9 Apr 2024 23:53:31 +0900 Subject: [PATCH 17/33] =?UTF-8?q?[#62]=20=EB=A9=A4=EB=B2=84=EB=A5=BC=20?= =?UTF-8?q?=EC=B0=BE=EA=B1=B0=EB=82=98=20=EC=A0=80=EC=9E=A5=ED=95=9C=20?= =?UTF-8?q?=ED=9B=84,=20=ED=86=A0=ED=81=B0=EC=9D=84=20=EB=B0=9C=EA=B8=89?= =?UTF-8?q?=ED=95=98=EB=8A=94=20service=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/auth/service/AuthService.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 backend/memetory/src/main/java/com/example/memetory/domain/auth/service/AuthService.java diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/auth/service/AuthService.java b/backend/memetory/src/main/java/com/example/memetory/domain/auth/service/AuthService.java new file mode 100644 index 00000000..364c6990 --- /dev/null +++ b/backend/memetory/src/main/java/com/example/memetory/domain/auth/service/AuthService.java @@ -0,0 +1,52 @@ +package com.example.memetory.domain.auth.service; + +import java.util.UUID; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import com.example.memetory.domain.auth.dto.LoginRequest; +import com.example.memetory.domain.auth.userInfo.OAuth2UserInfo; +import com.example.memetory.domain.member.entity.Member; +import com.example.memetory.domain.member.entity.Role; +import com.example.memetory.domain.member.entity.SocialType; +import com.example.memetory.domain.member.repository.MemberRepository; +import com.example.memetory.global.security.jwt.service.JwtService; + +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class AuthService { + private static final Logger log = LoggerFactory.getLogger(AuthService.class); + private final MemberRepository memberRepository; + private final OAuth2ProviderService oAuth2ProviderService; + private final JwtService jwtService; + + public void authenticateOrRegisterUser(LoginRequest loginRequest, HttpServletResponse response) { + OAuth2UserInfo userInfo = oAuth2ProviderService.getUserInfo(loginRequest); + Member member = findOrElseRegisterMember(userInfo, loginRequest.getSocialType()); + jwtService.sendAccessAndRefreshToken(response, member.getEmail()); + } + + private Member findOrElseRegisterMember(OAuth2UserInfo userInfo, SocialType socialType) { + return memberRepository.findBySocialTypeAndSocialId(socialType, userInfo.getId()) + .orElse(registerMember(socialType, userInfo)); + } + + private Member registerMember(SocialType socialType, OAuth2UserInfo userInfo) { + Member member = Member.builder() + .socialType(socialType) + .socialId(userInfo.getId()) + .email(UUID.randomUUID() + "@socialUser.com") + .name(userInfo.getName()) + .nickname(String.valueOf(UUID.randomUUID())) + .imageUrl(userInfo.getImageUrl()) + .role(Role.USER) + .build(); + + return memberRepository.save(member); + } +} From a658a9bc24bc348621a6752f0baa062c08d281f5 Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 9 Apr 2024 23:53:55 +0900 Subject: [PATCH 18/33] =?UTF-8?q?[#62]=20=EC=9D=B8=EC=A6=9D=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=EC=9D=B8=20Aut?= =?UTF-8?q?hController=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/auth/controller/AuthApi.java | 32 +++++++++++++++++++ .../auth/controller/AuthController.java | 25 +++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 backend/memetory/src/main/java/com/example/memetory/domain/auth/controller/AuthApi.java create mode 100644 backend/memetory/src/main/java/com/example/memetory/domain/auth/controller/AuthController.java diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/auth/controller/AuthApi.java b/backend/memetory/src/main/java/com/example/memetory/domain/auth/controller/AuthApi.java new file mode 100644 index 00000000..4f967af4 --- /dev/null +++ b/backend/memetory/src/main/java/com/example/memetory/domain/auth/controller/AuthApi.java @@ -0,0 +1,32 @@ +package com.example.memetory.domain.auth.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; + +import com.example.memetory.domain.auth.dto.LoginRequest; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +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 jakarta.servlet.http.HttpServletResponse; + +@Tag(name = "Auth") +public interface AuthApi { + + @Operation( + summary = "로그인", + description = "앱에서 받아온 인증서버의 access_token을 통해서 우리 서버의 JWT를 받아가는 과정" + + "처음 로그인 할 경우 DB에 사용자 등록이 진행된다." + ) + @ApiResponses( + @ApiResponse( + responseCode = "200", + description = "로그인 성공, header의 Authorization과 Authorization-refresh를 확인" + ) + ) + ResponseEntity login(LoginRequest request, + @Parameter(hidden = true) HttpServletResponse response); +} diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/auth/controller/AuthController.java b/backend/memetory/src/main/java/com/example/memetory/domain/auth/controller/AuthController.java new file mode 100644 index 00000000..6542ed2f --- /dev/null +++ b/backend/memetory/src/main/java/com/example/memetory/domain/auth/controller/AuthController.java @@ -0,0 +1,25 @@ +package com.example.memetory.domain.auth.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import com.example.memetory.domain.auth.dto.LoginRequest; +import com.example.memetory.domain.auth.service.AuthService; + +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +public class AuthController implements AuthApi { + private final AuthService authService; + + @PostMapping("/login") + public ResponseEntity login(@RequestBody LoginRequest request, HttpServletResponse response) { + authService.authenticateOrRegisterUser(request, response); + return new ResponseEntity<>(HttpStatus.OK); + } +} From fd7b5334342049162055099b9259ff0f50dd0509 Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 9 Apr 2024 23:54:32 +0900 Subject: [PATCH 19/33] =?UTF-8?q?[#62]=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EB=A1=9C=EC=A7=81=20=EC=82=AD=EC=A0=9C=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/controller/MemberApi.java | 27 ---------------- .../member/controller/MemberController.java | 31 ------------------- 2 files changed, 58 deletions(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberApi.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberApi.java index b1622736..2e9a482d 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberApi.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberApi.java @@ -1,35 +1,8 @@ package com.example.memetory.domain.member.controller; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; - -import com.example.memetory.domain.member.dto.MemberSignUpRequest; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletResponse; @Tag(name = "Member") public interface MemberApi { - @Operation( - summary = "회원가입", - description = "첫 소셜로그인 후 추가 정보 기입", - security = {@SecurityRequirement(name = "access_token")} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "회원가입 성공!" - ) - }) - ResponseEntity register( - HttpServletResponse response, - MemberSignUpRequest memberSignUpRequest, - @Parameter(hidden = true) String email - ); } diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java index 92a03b11..c510fc12 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java @@ -1,41 +1,10 @@ package com.example.memetory.domain.member.controller; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import com.example.memetory.domain.member.dto.MemberServiceDto; -import com.example.memetory.domain.member.dto.MemberSignUpRequest; -import com.example.memetory.domain.member.service.MemberService; -import com.example.memetory.global.annotation.LoginMemberEmail; -import com.example.memetory.global.security.jwt.refresh.service.RefreshTokenService; -import com.example.memetory.global.security.jwt.service.JwtService; - -import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @RestController @RequiredArgsConstructor public class MemberController implements MemberApi { - private final MemberService memberService; - private final JwtService jwtService; - private final RefreshTokenService refreshTokenService; - - @PostMapping("/sign-up") - @Override - public ResponseEntity register( - HttpServletResponse response, @RequestBody MemberSignUpRequest memberSignUpRequest, - @LoginMemberEmail String email) { - MemberServiceDto memberServiceDto = memberSignUpRequest.toServiceDto(email); - - memberService.register(memberServiceDto); - - String refreshToken = jwtService.createRefreshToken(); - jwtService.setRefreshTokenHeader(response, refreshToken); - refreshTokenService.updateToken(email, refreshToken); - - return ResponseEntity.status(HttpStatus.OK).build(); - } } From 13f9fc6c34a512736151aeb8e3a6879dbcb29c0b Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 9 Apr 2024 23:54:47 +0900 Subject: [PATCH 20/33] =?UTF-8?q?[#62]=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EB=A1=9C=EC=A7=81=20=EC=82=AD=EC=A0=9C=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20DTO=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/dto/MemberSignUpRequest.java | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 backend/memetory/src/main/java/com/example/memetory/domain/member/dto/MemberSignUpRequest.java diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/dto/MemberSignUpRequest.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/dto/MemberSignUpRequest.java deleted file mode 100644 index 647c892d..00000000 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/dto/MemberSignUpRequest.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.example.memetory.domain.member.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@AllArgsConstructor -@NoArgsConstructor(access = AccessLevel.PRIVATE) -@Getter -@Schema(description = "회원 가입 포맷") -public class MemberSignUpRequest { - - @Schema(description = "닉네임") - private String nickName; - - public MemberServiceDto toServiceDto(String email) { - return MemberServiceDto.builder() - .email(email) - .nickname(nickName) - .build(); - } -} From 20b76273ef4b66477279a39bdbe183edabbac15a Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 9 Apr 2024 23:55:34 +0900 Subject: [PATCH 21/33] =?UTF-8?q?[#62]=20Oauth2=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=EA=B4=80=20=EA=B4=80=EB=A0=A8=EB=90=9C=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/service/MemberService.java | 2 +- .../memetory/global/config/SecurityConfig.java | 14 +------------- .../example/memetory/global/config/WebConfig.java | 1 - 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/service/MemberService.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/service/MemberService.java index 83642344..3ddce642 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/service/MemberService.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/service/MemberService.java @@ -21,7 +21,7 @@ public class MemberService { public void register(MemberServiceDto memberServiceDto) { Member member = findByEmail(memberServiceDto.getEmail()); - member.register(memberServiceDto); + member.update(memberServiceDto); } @Transactional(readOnly = true) diff --git a/backend/memetory/src/main/java/com/example/memetory/global/config/SecurityConfig.java b/backend/memetory/src/main/java/com/example/memetory/global/config/SecurityConfig.java index cfcf7c75..a0910318 100644 --- a/backend/memetory/src/main/java/com/example/memetory/global/config/SecurityConfig.java +++ b/backend/memetory/src/main/java/com/example/memetory/global/config/SecurityConfig.java @@ -15,9 +15,6 @@ import com.example.memetory.global.security.jwt.filter.JwtAuthenticationProcessingFilter; import com.example.memetory.global.security.jwt.refresh.service.RefreshTokenService; import com.example.memetory.global.security.jwt.service.JwtService; -import com.example.memetory.global.security.oauth.handler.OAuth2LoginFailureHandler; -import com.example.memetory.global.security.oauth.handler.OAuth2LoginSuccessHandler; -import com.example.memetory.global.security.oauth.service.CustomOAuth2UserService; import lombok.RequiredArgsConstructor; @@ -29,9 +26,6 @@ public class SecurityConfig { private final JwtService jwtService; private final MemberRepository memberRepository; - private final OAuth2LoginSuccessHandler oAuth2LoginSuccessHandler; - private final OAuth2LoginFailureHandler oAuth2LoginFailureHandler; - private final CustomOAuth2UserService customOAuth2UserService; private final RefreshTokenService refreshTokenService; @Bean @@ -45,13 +39,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { ) .authorizeHttpRequests(authorizeRequests // 개발을 진행하기 위해 일단 모든 url 허용 -> authorizeRequests - .anyRequest().permitAll()) - .oauth2Login(oauth2 -> oauth2 // oauth2.0 설정 - .successHandler(oAuth2LoginSuccessHandler) - .failureHandler(oAuth2LoginFailureHandler) - // oauth2 로그인에 성공했을 떄 유저 정보를 가져올 때 설정을 담당 - .userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService)) - ); + .anyRequest().permitAll()); http.addFilterAfter(jwtAuthenticationProcessingFilter(), LogoutFilter.class); diff --git a/backend/memetory/src/main/java/com/example/memetory/global/config/WebConfig.java b/backend/memetory/src/main/java/com/example/memetory/global/config/WebConfig.java index 69e199dc..570e7d9d 100644 --- a/backend/memetory/src/main/java/com/example/memetory/global/config/WebConfig.java +++ b/backend/memetory/src/main/java/com/example/memetory/global/config/WebConfig.java @@ -10,7 +10,6 @@ import lombok.RequiredArgsConstructor; -// resolver를 위한 설정 @Configuration @RequiredArgsConstructor public class WebConfig implements WebMvcConfigurer { From c7c4f9de8c112764e428edc926d6a024fd256d1f Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 9 Apr 2024 23:56:10 +0900 Subject: [PATCH 22/33] =?UTF-8?q?[#62]=20Oauth2=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EB=B0=8F=20=ED=95=98=EC=9C=84=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/oauth/CustomOAuth2User.java | 26 ------ .../security/oauth/OAuthAttributes.java | 77 ----------------- .../handler/OAuth2LoginFailureHandler.java | 25 ------ .../handler/OAuth2LoginSuccessHandler.java | 59 ------------- .../service/CustomOAuth2UserService.java | 86 ------------------- 5 files changed, 273 deletions(-) delete mode 100644 backend/memetory/src/main/java/com/example/memetory/global/security/oauth/CustomOAuth2User.java delete mode 100644 backend/memetory/src/main/java/com/example/memetory/global/security/oauth/OAuthAttributes.java delete mode 100644 backend/memetory/src/main/java/com/example/memetory/global/security/oauth/handler/OAuth2LoginFailureHandler.java delete mode 100644 backend/memetory/src/main/java/com/example/memetory/global/security/oauth/handler/OAuth2LoginSuccessHandler.java delete mode 100644 backend/memetory/src/main/java/com/example/memetory/global/security/oauth/service/CustomOAuth2UserService.java diff --git a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/CustomOAuth2User.java b/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/CustomOAuth2User.java deleted file mode 100644 index c287f512..00000000 --- a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/CustomOAuth2User.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.example.memetory.global.security.oauth; - -import java.util.Collection; -import java.util.Map; - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.oauth2.core.user.DefaultOAuth2User; - -import com.example.memetory.domain.member.entity.Role; - -import lombok.Getter; - -@Getter -public class CustomOAuth2User extends DefaultOAuth2User { - - private String email; - private Role role; - - public CustomOAuth2User(Collection authorities, - Map attributes, String nameAttributeKey, - String email, Role role) { - super(authorities, attributes, nameAttributeKey); - this.email = email; - this.role = role; - } -} diff --git a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/OAuthAttributes.java b/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/OAuthAttributes.java deleted file mode 100644 index a7cf9ebd..00000000 --- a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/OAuthAttributes.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.example.memetory.global.security.oauth; - -import java.util.Map; -import java.util.UUID; - -import com.example.memetory.domain.member.entity.Member; -import com.example.memetory.domain.member.entity.Role; -import com.example.memetory.domain.member.entity.SocialType; -import com.example.memetory.global.security.oauth.userInfo.GoogleOAuth2UserInfo; -import com.example.memetory.global.security.oauth.userInfo.OAuth2UserInfo; - -import lombok.Builder; -import lombok.Getter; - -/** - * 각 소셜에서 받아오는 데이터가 다르므로 - * 소셜별로 데이터를 받는 데이터를 분기 처리하는 DTO 클래스 - */ -@Getter -public class OAuthAttributes { - - private String nameAttributeKey; // OAuth2 로그인 진행 시 키가 되는 필드 값, PK와 같은 의미 - private OAuth2UserInfo oauth2UserInfo; // 소셜 타입별 로그인 유저 정보(닉네임, 이메일, 프로필 사진 등등) - - @Builder - private OAuthAttributes(String nameAttributeKey, OAuth2UserInfo oauth2UserInfo) { - this.nameAttributeKey = nameAttributeKey; - this.oauth2UserInfo = oauth2UserInfo; - } - - /** - * SocialType에 맞는 메소드 호출하여 OAuthAttributes 객체 반환 - * 파라미터 : userNameAttributeName -> OAuth2 로그인 시 키(PK)가 되는 값 / attributes : OAuth 서비스의 유저 정보들 - * 소셜별 of 메소드(ofGoogle, ofKaKao, ofNaver)들은 각각 소셜 로그인 API에서 제공하는 - * 회원의 식별값(id), attributes, nameAttributeKey를 저장 후 build - */ - public static OAuthAttributes of(SocialType socialType, - String userNameAttributeName, Map attributes) { - - // if (socialType == SocialType.KAKAO) { - // return ofKakao(userNameAttributeName, attributes); - // } - return ofGoogle(userNameAttributeName, attributes); - } - - // private static OAuthAttributes ofKakao(String userNameAttributeName, Map attributes) { - // return OAuthAttributes.builder() - // .nameAttributeKey(userNameAttributeName) - // .oauth2UserInfo(new KakaoOAuth2UserInfo(attributes)) - // .build(); - // } - - public static OAuthAttributes ofGoogle(String userNameAttributeName, Map attributes) { - return OAuthAttributes.builder() - .nameAttributeKey(userNameAttributeName) - .oauth2UserInfo(new GoogleOAuth2UserInfo(attributes)) - .build(); - } - - /** - * of메소드로 OAuthAttributes 객체가 생성되어, 유저 정보들이 담긴 OAuth2UserInfo가 소셜 타입별로 주입된 상태 - * OAuth2UserInfo에서 socialId(식별값), nickname, imageUrl을 가져와서 build - * email에는 UUID로 중복 없는 랜덤 값 생성 - * role은 GUEST로 설정 - */ - public Member toEntity(SocialType socialType, OAuth2UserInfo oauth2UserInfo) { - return Member.builder() - .socialType(socialType) - .socialId(oauth2UserInfo.getId()) - .email(UUID.randomUUID() + "@socialUser.com") - .nickname(oauth2UserInfo.getNickname()) - // 이미지는 기본 이미지로 할 것인지, 아니면 뺄 것인지 정해야 함. - .imageUrl(oauth2UserInfo.getImageUrl()) - .role(Role.GUEST) - .build(); - } -} \ No newline at end of file diff --git a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/handler/OAuth2LoginFailureHandler.java b/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/handler/OAuth2LoginFailureHandler.java deleted file mode 100644 index 38a32c21..00000000 --- a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/handler/OAuth2LoginFailureHandler.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.example.memetory.global.security.oauth.handler; - -import java.io.IOException; - -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.authentication.AuthenticationFailureHandler; -import org.springframework.stereotype.Component; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@Component -public class OAuth2LoginFailureHandler implements AuthenticationFailureHandler { - // 로그인 실패 Exception 구현 - @Override - public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, - AuthenticationException exception) throws IOException { - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - response.getWriter().write("소셜 로그인 실패! 서버 로그를 확인해주세요."); - log.info("소셜 로그인에 실패했습니다. 에러 메시지 : {}", exception.getMessage()); - } -} - diff --git a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/handler/OAuth2LoginSuccessHandler.java b/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/handler/OAuth2LoginSuccessHandler.java deleted file mode 100644 index d3d95781..00000000 --- a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/handler/OAuth2LoginSuccessHandler.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.example.memetory.global.security.oauth.handler; - -import com.example.memetory.domain.member.entity.Role; -import com.example.memetory.domain.member.repository.MemberRepository; -import com.example.memetory.global.security.jwt.service.JwtService; -import com.example.memetory.global.security.oauth.CustomOAuth2User; - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -import org.springframework.security.core.Authentication; -import org.springframework.security.web.authentication.AuthenticationSuccessHandler; -import org.springframework.stereotype.Component; - -import java.io.IOException; - -@Slf4j -@Component -@RequiredArgsConstructor -public class OAuth2LoginSuccessHandler implements AuthenticationSuccessHandler { - - private final JwtService jwtService; - - @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) throws IOException, ServletException { - log.info("OAuth2 Login 성공!"); - try { - CustomOAuth2User oAuth2User = (CustomOAuth2User)authentication.getPrincipal(); - - // User의 Role이 GUEST일 경우 처음 요청한 회원이므로 회원가입 페이지로 리다이렉트 - // header에 accesstoken은 잘 넘어가는지, 페이지는 알맞게 변경되는지 등을 고려해서 다시 봐야 함. - if (oAuth2User.getRole() == Role.GUEST) { - String accessToken = jwtService.createAccessToken(oAuth2User.getEmail()); - response.addHeader(jwtService.getAccessHeader(), "Bearer " + accessToken); - // response.sendRedirect("/sign-up"); // 프론트의 회원가입 추가 정보 입력 폼으로 리다이렉트 - - jwtService.setAccessTokenHeader(response, accessToken); - - } else { - loginSuccess(response, oAuth2User); // 로그인에 성공한 경우 access, refresh 토큰 생성 - } - } catch (Exception e) { - throw e; - } - - } - - private void loginSuccess(HttpServletResponse response, CustomOAuth2User oAuth2User) throws IOException { - String accessToken = jwtService.createAccessToken(oAuth2User.getEmail()); - String refreshToken = jwtService.createRefreshToken(); - - jwtService.sendAccessAndRefreshToken(response, accessToken, refreshToken); - jwtService.updateRefreshToken(oAuth2User.getEmail(), refreshToken); - } -} diff --git a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/service/CustomOAuth2UserService.java b/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/service/CustomOAuth2UserService.java deleted file mode 100644 index a258860f..00000000 --- a/backend/memetory/src/main/java/com/example/memetory/global/security/oauth/service/CustomOAuth2UserService.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.example.memetory.global.security.oauth.service; - -import java.util.Collections; -import java.util.Map; - -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; -import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; -import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.stereotype.Service; - -import com.example.memetory.domain.member.entity.Member; -import com.example.memetory.domain.member.entity.SocialType; -import com.example.memetory.domain.member.repository.MemberRepository; -import com.example.memetory.global.security.oauth.CustomOAuth2User; -import com.example.memetory.global.security.oauth.OAuthAttributes; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@Service -@RequiredArgsConstructor -public class CustomOAuth2UserService implements OAuth2UserService { - - private final MemberRepository memberRepository; - - // private static final String KAKAO = "kakao"; - - @Override - public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException { - log.info("CustomOAuth2UserService.loadUser() 실행 - OAuth2 로그인 요청 진입"); - - OAuth2UserService delegate = new DefaultOAuth2UserService(); - OAuth2User oAuth2User = delegate.loadUser(userRequest); - - /** - * userRequest에서 registrationId 추출 후 registrationId으로 SocialType 저장 - * http://localhost:8080/oauth2/authorization/kakao에서 kakao가 registrationId - * userNameAttributeName은 이후에 nameAttributeKey로 설정된다. - */ - String registrationId = userRequest.getClientRegistration().getRegistrationId(); - SocialType socialType = getSocialType(registrationId); - String userNameAttributeName = userRequest.getClientRegistration() - .getProviderDetails().getUserInfoEndpoint().getUserNameAttributeName(); // OAuth2 로그인 시 키(PK)가 되는 값 - Map attributes = oAuth2User.getAttributes(); // 소셜 로그인에서 API가 제공하는 userInfo의 Json 값(유저 정보들) - - // socialType에 따라 유저 정보를 통해 OAuthAttributes 객체 생성 - OAuthAttributes extractAttributes = OAuthAttributes.of(socialType, userNameAttributeName, attributes); - - Member createdUser = getMember(extractAttributes, socialType); // getUser() 메소드로 User 객체 생성 후 반환 - - // DefaultOAuth2User를 구현한 CustomOAuth2User 객체를 생성해서 반환 - return new CustomOAuth2User( - Collections.singleton(new SimpleGrantedAuthority(createdUser.getRole().getKey())), - attributes, - extractAttributes.getNameAttributeKey(), - createdUser.getEmail(), - createdUser.getRole() - ); - } - - private SocialType getSocialType(String registrationId) { - // if(KAKAO.equals(registrationId)) { - // return SocialType.KAKAO; - // } - return SocialType.GOOGLE; - } - - private Member getMember(OAuthAttributes attributes, SocialType socialType) { - Member findMember = memberRepository.findBySocialTypeAndSocialId(socialType, - attributes.getOauth2UserInfo().getId()).orElse(null); - - if (findMember == null) { - return saveMember(attributes, socialType); - } - return findMember; - } - - private Member saveMember(OAuthAttributes attributes, SocialType socialType) { - Member createdMember = attributes.toEntity(socialType, attributes.getOauth2UserInfo()); - return memberRepository.save(createdMember); - } -} From 7ee04585fe69ffd3a001b7359f509aba995b51bf Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 9 Apr 2024 23:56:21 +0900 Subject: [PATCH 23/33] =?UTF-8?q?[#62]=20Oauth2=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/memetory/src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/memetory/src/main/resources/application.yml b/backend/memetory/src/main/resources/application.yml index 8593ae3d..bb8150c4 100644 --- a/backend/memetory/src/main/resources/application.yml +++ b/backend/memetory/src/main/resources/application.yml @@ -1,7 +1,7 @@ spring: profiles: group: - "local" : "local, jwt, oauth" + "local" : "local, jwt" active : local # "local" 을 실행 --- From 86afd6910330f725730fea784f259a89cba5dcd3 Mon Sep 17 00:00:00 2001 From: JunRain Date: Tue, 9 Apr 2024 23:56:31 +0900 Subject: [PATCH 24/33] =?UTF-8?q?[#62]=20Oauth2=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/memetory/build.gradle | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/memetory/build.gradle b/backend/memetory/build.gradle index 93d529c4..919746ba 100644 --- a/backend/memetory/build.gradle +++ b/backend/memetory/build.gradle @@ -36,7 +36,6 @@ dependencies { implementation 'com.auth0:java-jwt:4.2.1' implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' // Swagger 적용 implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' From 2d87a0b7f6275b51bcb5f65f3b236d49f6b68358 Mon Sep 17 00:00:00 2001 From: JunRain Date: Wed, 10 Apr 2024 01:03:37 +0900 Subject: [PATCH 25/33] =?UTF-8?q?[#62]=20Member=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EC=97=A1=EB=8D=B0=EC=9D=B4=ED=8A=B8=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/memetory/domain/member/entity/Member.java | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/Member.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/Member.java index 02f0da48..a49bb9c9 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/Member.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/entity/Member.java @@ -55,5 +55,6 @@ public Member(String email, String nickname, String name, String imageUrl, Role // Todo 닉네임이랑, 이미지 변경할 수 있게 하기 public void update(MemberServiceDto memberServiceDto) { this.nickname = memberServiceDto.getNickname(); + this.imageUrl = memberServiceDto.getImageUrl(); } } From 8ed84fc6a94b40233af200473ff71e66b81a2b84 Mon Sep 17 00:00:00 2001 From: JunRain Date: Wed, 10 Apr 2024 01:04:45 +0900 Subject: [PATCH 26/33] =?UTF-8?q?[#62]=20MemberRepository=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=8B=89=EB=84=A4=EC=9E=84=EC=9D=B4=20=EC=A1=B4?= =?UTF-8?q?=EC=9E=AC=EC=97=AC=EB=B6=80=20=EB=B0=98=ED=99=98=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../memetory/domain/member/repository/MemberRepository.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/repository/MemberRepository.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/repository/MemberRepository.java index 2dc733a5..c4a362d1 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/repository/MemberRepository.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/repository/MemberRepository.java @@ -10,11 +10,7 @@ public interface MemberRepository extends JpaRepository { Optional findByEmail(String email); - Optional findByNickname(String nickname); + boolean existsMemberByNickname(String nickname); - /** - * 소셜 타입과 소셜의 식별값으로 회원 찾는 메소드 - * 추가 정보를 입력받아 회원 가입을 진행할 때 소셜 타입, 식별자로 해당 회원을 찾기 위한 메소드 - */ Optional findBySocialTypeAndSocialId(SocialType socialType, String socialId); } From 845946877de24a52203fc1904877311ff76dc6fe Mon Sep 17 00:00:00 2001 From: JunRain Date: Wed, 10 Apr 2024 01:05:24 +0900 Subject: [PATCH 27/33] =?UTF-8?q?[#62]=20Member=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20=EA=B4=80=EB=A0=A8=20DTO=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/dto/MemberUpdateDto.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 backend/memetory/src/main/java/com/example/memetory/domain/member/dto/MemberUpdateDto.java diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/dto/MemberUpdateDto.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/dto/MemberUpdateDto.java new file mode 100644 index 00000000..6b2efa2a --- /dev/null +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/dto/MemberUpdateDto.java @@ -0,0 +1,26 @@ +package com.example.memetory.domain.member.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor +@Schema(description = "멤버 업데이트 포맷") +public class MemberUpdateDto { + @Schema(description = "변경할 닉네임") + private String nickname; + @Schema(description = "변경할 이미지 S3 Url") + private String imageUrl; + + public MemberServiceDto toServiceDto(String email) { + return MemberServiceDto.builder() + .email(email) + .nickname(this.nickname) + .imageUrl(this.imageUrl) + .build(); + } +} From 840d25277b40d0cff419743cb2c4cfb2d1f307a8 Mon Sep 17 00:00:00 2001 From: JunRain Date: Wed, 10 Apr 2024 01:05:41 +0900 Subject: [PATCH 28/33] =?UTF-8?q?[#62]=20Member=20=EB=8B=89=EB=84=A4?= =?UTF-8?q?=EC=9E=84=20=EC=A4=91=EB=B3=B5=20=EC=97=AC=EB=B6=80=20=EB=B0=8F?= =?UTF-8?q?=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../memetory/domain/member/service/MemberService.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/service/MemberService.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/service/MemberService.java index 3ddce642..a4adba39 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/service/MemberService.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/service/MemberService.java @@ -18,10 +18,8 @@ public class MemberService { private final MemberRepository memberRepository; @Transactional - public void register(MemberServiceDto memberServiceDto) { - Member member = findByEmail(memberServiceDto.getEmail()); - - member.update(memberServiceDto); + public void update(MemberServiceDto memberServiceDto) { + findByEmail(memberServiceDto.getEmail()).update(memberServiceDto); } @Transactional(readOnly = true) @@ -33,4 +31,9 @@ public Member findByEmail(String email) { public Member findById(Long id) { return memberRepository.findById(id).orElseThrow(NotFoundMemberException::new); } + + @Transactional(readOnly = true) + public boolean isDuplicatedNickname(MemberServiceDto memberServiceDto) { + return memberRepository.existsMemberByNickname(memberServiceDto.getNickname()); + } } From a5ece19888a1b0d6404aafea4f9f15d437783031 Mon Sep 17 00:00:00 2001 From: JunRain Date: Wed, 10 Apr 2024 01:06:08 +0900 Subject: [PATCH 29/33] =?UTF-8?q?[#62]=20MemberController=EC=97=90?= =?UTF-8?q?=EC=84=9C=20Member=20update=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java index c510fc12..ae640d62 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java @@ -1,10 +1,35 @@ package com.example.memetory.domain.member.controller; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.example.memetory.domain.member.dto.MemberServiceDto; +import com.example.memetory.domain.member.dto.MemberUpdateDto; +import com.example.memetory.domain.member.service.MemberService; +import com.example.memetory.global.annotation.LoginMemberEmail; + import lombok.RequiredArgsConstructor; @RestController @RequiredArgsConstructor +@RequestMapping("/member") public class MemberController implements MemberApi { + private final MemberService memberService; + + @PostMapping + ResponseEntity updateMember(@LoginMemberEmail String email, + @RequestBody MemberUpdateDto memberUpdateDto) { + MemberServiceDto memberServiceDto = memberUpdateDto.toServiceDto(email); + + if (memberService.isDuplicatedNickname(memberServiceDto)) { + return ResponseEntity.status(HttpStatus.CONFLICT).build(); + } + memberService.update(memberServiceDto); + + return ResponseEntity.status(HttpStatus.OK).build(); + } } From 11681a256c18b601b0a3874ddf91c8cc6b59dc93 Mon Sep 17 00:00:00 2001 From: JunRain Date: Wed, 10 Apr 2024 01:06:49 +0900 Subject: [PATCH 30/33] =?UTF-8?q?[#62]=20MemberRepository=20=EB=8B=89?= =?UTF-8?q?=EB=84=A4=EC=9E=84=20=EC=A4=91=EB=B3=B5=20=EC=97=AC=EB=B6=80=20?= =?UTF-8?q?Test=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/repository/MemberRepositoryTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/member/repository/MemberRepositoryTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/member/repository/MemberRepositoryTest.java index 26b1b2b4..e7350fb4 100644 --- a/backend/memetory/src/test/java/com/example/memetory/domain/member/repository/MemberRepositoryTest.java +++ b/backend/memetory/src/test/java/com/example/memetory/domain/member/repository/MemberRepositoryTest.java @@ -2,6 +2,7 @@ import static com.example.memetory.domain.member.MemberFixture.*; import static org.assertj.core.api.AssertionsForClassTypes.*; +import static org.junit.jupiter.api.Assertions.*; import java.util.Optional; @@ -49,4 +50,14 @@ public class MemberRepositoryTest { // then 확인하기 assertThat(savedMember).isEqualTo(findMember.get()); } + + @Test + @DisplayName("닉네임 존재하는지 확인하기") + public void 닉네임_존재_여부() { + // given -> 멤버 저장하기 + Member savedMember = memberRepository.save(MEMBER); + + // then 확인하기 + assertTrue(memberRepository.existsMemberByNickname(MEMBER.getNickname())); + } } From 83695a5e4c9e2cbb41dc5d24c3f14a0a27880274 Mon Sep 17 00:00:00 2001 From: JunRain Date: Wed, 10 Apr 2024 01:08:00 +0900 Subject: [PATCH 31/33] =?UTF-8?q?[#62]=20MemberService=20=EB=8B=89?= =?UTF-8?q?=EB=84=A4=EC=9E=84=20=EC=A4=91=EB=B3=B5=20=EC=97=AC=EB=B6=80=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=20=EB=B0=8F=20Member=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=20test=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../memetory/domain/member/MemberFixture.java | 11 +---- .../member/service/MemberServiceTest.java | 48 +++++++++++-------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/member/MemberFixture.java b/backend/memetory/src/test/java/com/example/memetory/domain/member/MemberFixture.java index d4fb3d18..4518bd99 100644 --- a/backend/memetory/src/test/java/com/example/memetory/domain/member/MemberFixture.java +++ b/backend/memetory/src/test/java/com/example/memetory/domain/member/MemberFixture.java @@ -13,19 +13,12 @@ public class MemberFixture { .email("junrain@ourservice.com") .role(Role.USER) .imageUrl("imageUrl") - .nickname("이준우") + .name("이준우") + .nickname("junRain") .socialType(SocialType.GOOGLE) .socialId("-1") .build(); - public final static Member GUEST_MEMBER = Member.builder() - .email("junrain@ourservice.com") - .role(Role.GUEST) - .imageUrl("imageUrl") - .nickname("이준우") - .socialType(SocialType.GOOGLE) - .socialId("-1") - .build(); public final static MemberServiceDto MEMBER_SERVICE_DTO = MemberServiceDto.builder() .imageUrl("imageUrl") diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/member/service/MemberServiceTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/member/service/MemberServiceTest.java index 3a10ade7..a7ba57f0 100644 --- a/backend/memetory/src/test/java/com/example/memetory/domain/member/service/MemberServiceTest.java +++ b/backend/memetory/src/test/java/com/example/memetory/domain/member/service/MemberServiceTest.java @@ -2,8 +2,7 @@ import static com.example.memetory.domain.member.MemberFixture.*; import static org.assertj.core.api.Assertions.*; - -import java.util.Optional; +import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -12,9 +11,7 @@ import org.springframework.transaction.annotation.Transactional; import com.example.memetory.domain.member.entity.Member; -import com.example.memetory.domain.member.entity.Role; import com.example.memetory.domain.member.repository.MemberRepository; -import com.example.memetory.global.security.jwt.service.JwtService; @DisplayName("memberService의 ") @Transactional @@ -23,25 +20,8 @@ public class MemberServiceTest { @Autowired private MemberService memberService; @Autowired - private JwtService jwtService; - @Autowired private MemberRepository memberRepository; - @Test - @DisplayName("회원가입이 잘 진행 되는가?") - void 회원가입() { - // given 멤버 저장 - Member savedMember = memberRepository.save(GUEST_MEMBER); - - // when 실행 - memberService.register(MEMBER_SERVICE_DTO); - - // then 확인 ROLE과 닉네임이 잘 변경 됐는지 확인 - Optional updatedMember = memberRepository.findByEmail(GUEST_MEMBER.getEmail()); - assertThat(MEMBER_SERVICE_DTO.getNickname()).isEqualTo(updatedMember.get().getNickname()); - assertThat(Role.USER).isEqualTo(updatedMember.get().getRole()); - } - @Test @DisplayName("이메일을 통해서 멤버 가져오기") void 이메일을_통해서_Member_불러오기() { @@ -68,4 +48,30 @@ public class MemberServiceTest { // then assertThat(findMember.getId()).isEqualTo(savedMember.getId()); } + + @Test + @DisplayName("Member의 nickname 중복 여부 검사") + void nickname_중복검사() { + // given 멤버 저장 + Member savedMember = memberRepository.save(MEMBER); + + // then 실행 + assertTrue(memberService.isDuplicatedNickname(MEMBER_SERVICE_DTO)); + } + + @Test + @DisplayName("Member의 업데이트가 잘 이루어지는가") + void member_업데이트() { + // given 멤버 저장 + Member savedMember = memberRepository.save(MEMBER); + + // when 멤버 업데이트 + memberService.update(UPDATE_MEMBER_SERVICE_DTO); + + Member findMember = memberService.findByEmail(UPDATE_MEMBER_SERVICE_DTO.getEmail()); + // then 업데이트 됐는지 확인 + + assertThat(findMember.getNickname()).isEqualTo(UPDATE_MEMBER_SERVICE_DTO.getNickname()); + assertThat(findMember.getImageUrl()).isEqualTo(UPDATE_MEMBER_SERVICE_DTO.getImageUrl()); + } } From 895595bb15144b551a4c03ec6b3f81990a9186f1 Mon Sep 17 00:00:00 2001 From: JunRain Date: Wed, 10 Apr 2024 01:08:27 +0900 Subject: [PATCH 32/33] =?UTF-8?q?[#62]=20MemberController=20POST=20/member?= =?UTF-8?q?=20test=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../memetory/config/SecurityTestConfig.java | 1 - .../memetory/domain/member/MemberFixture.java | 6 +++ .../controller/MemberControllerTest.java | 42 +++++++++++++------ 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/backend/memetory/src/test/java/com/example/memetory/config/SecurityTestConfig.java b/backend/memetory/src/test/java/com/example/memetory/config/SecurityTestConfig.java index abc1e18a..3015cabe 100644 --- a/backend/memetory/src/test/java/com/example/memetory/config/SecurityTestConfig.java +++ b/backend/memetory/src/test/java/com/example/memetory/config/SecurityTestConfig.java @@ -2,7 +2,6 @@ import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/member/MemberFixture.java b/backend/memetory/src/test/java/com/example/memetory/domain/member/MemberFixture.java index 4518bd99..f7bb99a8 100644 --- a/backend/memetory/src/test/java/com/example/memetory/domain/member/MemberFixture.java +++ b/backend/memetory/src/test/java/com/example/memetory/domain/member/MemberFixture.java @@ -25,4 +25,10 @@ public class MemberFixture { .nickname("junRain") .email("junrain@ourservice.com") .build(); + + public final static MemberServiceDto UPDATE_MEMBER_SERVICE_DTO = MemberServiceDto.builder() + .imageUrl("imageUrl2") + .nickname("junRain2") + .email("junrain@ourservice.com") + .build(); } diff --git a/backend/memetory/src/test/java/com/example/memetory/domain/member/controller/MemberControllerTest.java b/backend/memetory/src/test/java/com/example/memetory/domain/member/controller/MemberControllerTest.java index 762a13ec..f3a075b2 100644 --- a/backend/memetory/src/test/java/com/example/memetory/domain/member/controller/MemberControllerTest.java +++ b/backend/memetory/src/test/java/com/example/memetory/domain/member/controller/MemberControllerTest.java @@ -2,7 +2,6 @@ import static com.example.memetory.domain.member.MemberFixture.*; import static org.mockito.BDDMockito.*; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -24,7 +23,7 @@ import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.example.memetory.config.SecurityTestConfig; -import com.example.memetory.domain.member.dto.MemberSignUpRequest; +import com.example.memetory.domain.member.dto.MemberUpdateDto; import com.example.memetory.domain.member.repository.MemberRepository; import com.example.memetory.domain.member.service.MemberService; import com.example.memetory.global.security.jwt.refresh.service.RefreshTokenService; @@ -43,11 +42,11 @@ public class MemberControllerTest { private ObjectMapper objectMapper; @MockBean - private JwtService jwtService; + private RefreshTokenService refreshTokenService; @MockBean private MemberService memberService; @MockBean - private RefreshTokenService refreshTokenService; + private JwtService jwtService; @MockBean private MemberRepository memberRepository; @@ -61,22 +60,25 @@ void setUp() { authorization_jwt = JWT.create() .withSubject("AccessToken") .withExpiresAt(new Date(now.getTime() + 180000)) - .withClaim("email", GUEST_MEMBER.getId()) + .withClaim("email", MEMBER.getEmail()) .sign(Algorithm.HMAC512(secretKey)); } - // 잘 된 테스트인지는 아직 의문 + protected String toRequestBody(Object value) throws JsonProcessingException { + return objectMapper.writeValueAsString(value); + } + @Test - @DisplayName("회원가입이 완료되었는가") - void 회원가입() throws Exception { + @DisplayName("멤버 업데이트 성공") + public void 멤버_업데이트_성공() throws Exception { // given -> 결과에 대한 객체, Member 객체 저장할 필요 존재 - given(memberRepository.findByEmail(GUEST_MEMBER.getEmail())).willReturn(Optional.ofNullable(GUEST_MEMBER)); + given(memberRepository.findByEmail(MEMBER.getEmail())).willReturn(Optional.ofNullable(MEMBER)); // when final ResultActions perform = mockMvc.perform( - post("/sign-up") + post("/member") .contentType(MediaType.APPLICATION_JSON) - .content(toRequestBody(new MemberSignUpRequest("Memetory"))) + .content(toRequestBody(new MemberUpdateDto("junrain2", "imageUrl2"))) .header("Authorization", "Bearer " + authorization_jwt) ); @@ -84,7 +86,21 @@ void setUp() { perform.andExpect(status().isOk()); } - protected String toRequestBody(Object value) throws JsonProcessingException { - return objectMapper.writeValueAsString(value); + @Test + @DisplayName("멤버 업데이트 성공(닉네임 중복)") + public void 멤버_업데이트_실패_닉네임_중복() throws Exception { + // given -> 결과에 대한 객체, Member 객체 저장할 필요 존재 + given(memberService.isDuplicatedNickname(any())).willReturn(true); + + // when + final ResultActions perform = mockMvc.perform( + post("/member") + .contentType(MediaType.APPLICATION_JSON) + .content(toRequestBody(new MemberUpdateDto("junrain", "imageUrl"))) + .header("Authorization", "Bearer " + authorization_jwt) + ); + + // then + perform.andExpect(status().isConflict()); } } From c9e73a9083e6b20f825953032ea4b68a3fa4d685 Mon Sep 17 00:00:00 2001 From: JunRain Date: Wed, 10 Apr 2024 01:18:45 +0900 Subject: [PATCH 33/33] =?UTF-8?q?[#62]=20MemberController=20POST=20/member?= =?UTF-8?q?=20API=20=EB=AC=B8=EC=84=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/auth/dto/LoginRequest.java | 2 +- .../domain/member/controller/MemberApi.java | 28 ++++++++++++++++++- .../member/controller/MemberController.java | 2 +- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/auth/dto/LoginRequest.java b/backend/memetory/src/main/java/com/example/memetory/domain/auth/dto/LoginRequest.java index e5b0ca3e..e5ea7872 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/auth/dto/LoginRequest.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/auth/dto/LoginRequest.java @@ -13,6 +13,6 @@ public class LoginRequest { @Schema(description = "인증서버에서 받아온 access token을 입력") private String token; - @Schema(description = "인증서버를 소문자로 작성, 현재는 google만 가능") + @Schema(description = "인증서버타입, 현재는 google만 가능") private SocialType socialType; } diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberApi.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberApi.java index 2e9a482d..ee23a4c1 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberApi.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberApi.java @@ -1,8 +1,34 @@ package com.example.memetory.domain.member.controller; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + +import com.example.memetory.domain.member.dto.MemberUpdateDto; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; @Tag(name = "Member") public interface MemberApi { - + @Operation( + summary = "멤버 업데이트", + description = "멤버의 이미지 url 및 닉네임 업데이트" + + "업데이트를 하지 않을 필드의 경우에는, 기존 값을 넣어 주면 된다.", + security = {@SecurityRequirement(name = "access_token")} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "이미지 생성 성공" + ), + @ApiResponse( + responseCode = "409", + description = "닉네임 중복" + )} + ) + ResponseEntity updateMember(@Parameter(hidden = true) String email, MemberUpdateDto memberUpdateDto); } diff --git a/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java b/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java index ae640d62..1771918f 100644 --- a/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java +++ b/backend/memetory/src/main/java/com/example/memetory/domain/member/controller/MemberController.java @@ -21,7 +21,7 @@ public class MemberController implements MemberApi { private final MemberService memberService; @PostMapping - ResponseEntity updateMember(@LoginMemberEmail String email, + public ResponseEntity updateMember(@LoginMemberEmail String email, @RequestBody MemberUpdateDto memberUpdateDto) { MemberServiceDto memberServiceDto = memberUpdateDto.toServiceDto(email);