diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index cc318977..f50fd747 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -32,11 +32,15 @@ include::{snippets}/social_login/success/response-fields.adoc[] .유효한 토큰이 아닌 경우 include::{snippets}/social_login/fail/invalid-token/response-body.adoc[] + .토큰이 전달되지 않은 경우 include::{snippets}/social_login/fail/not-exist-header/response-body.adoc[] - include::{snippets}/social_login/fail/invalid-token/response-fields.adoc[] +.전달한 JWT 토큰의 서명이 유효하지 않은 경우 +include::{snippets}/social_login/fail/invalid-signature/response-body.adoc[] +include::{snippets}/social_login/fail/invalid-signature/response-fields.adoc[] + === 로그아웃 로그아웃 시 사용되는 API입니다. @@ -144,6 +148,18 @@ include::{snippets}/validate_nickname/fail/invalid/response-fields.adoc[] include::{snippets}/validate_nickname/fail/duplicate/response-body.adoc[] include::{snippets}/validate_nickname/fail/duplicate/response-fields.adoc[] +=== 내 정보 조회 + +==== GET /api/v1/members/me + +===== 요청 +include::{snippets}/get-my-profile/success/http-request.adoc[] +include::{snippets}/get-my-profile/success/request-headers.adoc[] + +===== 응답 (200) +include::{snippets}/get-my-profile/success/response-body.adoc[] +include::{snippets}/get-my-profile/success/response-fields.adoc[] + === 사용자 정보 수정 ==== PATCH /api/v1/members/me diff --git a/src/main/java/com/stumeet/server/common/auth/exception/IllegalKeyAlgorithmException.java b/src/main/java/com/stumeet/server/common/auth/exception/IllegalKeyAlgorithmException.java new file mode 100644 index 00000000..0cc34fe6 --- /dev/null +++ b/src/main/java/com/stumeet/server/common/auth/exception/IllegalKeyAlgorithmException.java @@ -0,0 +1,13 @@ +package com.stumeet.server.common.auth.exception; + +import com.stumeet.server.common.response.ErrorCode; +import org.springframework.security.core.AuthenticationException; + +public class IllegalKeyAlgorithmException extends AuthenticationException { + private final ErrorCode errorCode; + + public IllegalKeyAlgorithmException(ErrorCode errorCode, Throwable cause) { + super(errorCode.getMessage(), cause); + this.errorCode = errorCode; + } +} diff --git a/src/main/java/com/stumeet/server/common/auth/exception/JwtInvalidSignatureException.java b/src/main/java/com/stumeet/server/common/auth/exception/JwtInvalidSignatureException.java new file mode 100644 index 00000000..2980adf3 --- /dev/null +++ b/src/main/java/com/stumeet/server/common/auth/exception/JwtInvalidSignatureException.java @@ -0,0 +1,16 @@ +package com.stumeet.server.common.auth.exception; + +import com.stumeet.server.common.response.ErrorCode; +import org.springframework.security.core.AuthenticationException; + + +public class JwtInvalidSignatureException extends AuthenticationException { + + private final ErrorCode errorCode; + + public JwtInvalidSignatureException(ErrorCode errorCode, Throwable e) { + super(errorCode.getMessage(), e); + this.errorCode = errorCode; + } + +} diff --git a/src/main/java/com/stumeet/server/common/auth/exception/JwtTokenParsingException.java b/src/main/java/com/stumeet/server/common/auth/exception/JwtTokenParsingException.java new file mode 100644 index 00000000..ce6dbf63 --- /dev/null +++ b/src/main/java/com/stumeet/server/common/auth/exception/JwtTokenParsingException.java @@ -0,0 +1,13 @@ +package com.stumeet.server.common.auth.exception; + +import com.stumeet.server.common.response.ErrorCode; +import org.springframework.security.core.AuthenticationException; + +public class JwtTokenParsingException extends AuthenticationException { + private final ErrorCode errorCode; + + public JwtTokenParsingException(ErrorCode errorCode, Throwable e) { + super(errorCode.getMessage(), e); + this.errorCode = errorCode; + } +} diff --git a/src/main/java/com/stumeet/server/common/client/oauth/apple/AppleIdTokenProvider.java b/src/main/java/com/stumeet/server/common/client/oauth/apple/AppleIdTokenProvider.java index 77d881c4..93890acc 100644 --- a/src/main/java/com/stumeet/server/common/client/oauth/apple/AppleIdTokenProvider.java +++ b/src/main/java/com/stumeet/server/common/client/oauth/apple/AppleIdTokenProvider.java @@ -1,8 +1,14 @@ package com.stumeet.server.common.client.oauth.apple; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.stumeet.server.common.client.oauth.apple.model.ApplePublicKeyResponses; +import com.stumeet.server.common.auth.exception.IllegalKeyAlgorithmException; +import com.stumeet.server.common.auth.exception.JwtInvalidSignatureException; +import com.stumeet.server.common.auth.exception.JwtTokenParsingException; +import com.stumeet.server.common.response.ErrorCode; import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.SignatureException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -36,7 +42,7 @@ public PublicKey getSecretKey(ApplePublicKeyResponses publicKeys, String accessT return keyFactory.generatePublic(keySpec); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - throw new RuntimeException(e); + throw new IllegalKeyAlgorithmException(ErrorCode.ILLEGAL_KEY_ALGORITHM_EXCEPTION, e); } } @@ -45,18 +51,22 @@ private String extractKid(String accessToken) { String header = new String(Base64.getUrlDecoder().decode(splitToken[0])); try { return objectMapper.readTree(header).get("kid").asText(); - } catch (Exception e) { - throw new RuntimeException(e); + } catch (JsonProcessingException e ) { + throw new JwtTokenParsingException(ErrorCode.JWT_TOKEN_PARSING_EXCEPTION, e); } } public String extractUserId(PublicKey publicKey, String idToken) { - return Jwts.parser() - .verifyWith(publicKey) - .build() - .parseSignedClaims(idToken) - .getPayload() - .getSubject(); + try { + return Jwts.parser() + .verifyWith(publicKey) + .build() + .parseSignedClaims(idToken) + .getPayload() + .getSubject(); + } catch (SignatureException e) { + throw new JwtInvalidSignatureException(ErrorCode.JWT_INVALID_SIGNATURE_EXCEPTION, e); + } } } diff --git a/src/main/java/com/stumeet/server/common/model/ApiResponse.java b/src/main/java/com/stumeet/server/common/model/ApiResponse.java index 1fb37639..ecea7f9e 100644 --- a/src/main/java/com/stumeet/server/common/model/ApiResponse.java +++ b/src/main/java/com/stumeet/server/common/model/ApiResponse.java @@ -37,6 +37,7 @@ public static ApiResponse> fail(ErrorCode errorCode, BindingResult b } // TODO: 삭제 예정 메서드 : 해당 메서드를 사용 부분 수정 요망 + public static ApiResponse success(int code, String message, T data) { return new ApiResponse<>(code, message, data); } @@ -48,4 +49,5 @@ public static ApiResponse success(int code, String message) { public static ApiResponse fail(int code, String message) { return new ApiResponse<>(code, message, null); } + } diff --git a/src/main/java/com/stumeet/server/common/response/ErrorCode.java b/src/main/java/com/stumeet/server/common/response/ErrorCode.java index b52e9ba4..a75fba36 100644 --- a/src/main/java/com/stumeet/server/common/response/ErrorCode.java +++ b/src/main/java/com/stumeet/server/common/response/ErrorCode.java @@ -32,6 +32,11 @@ public enum ErrorCode { */ ACCESS_DENIED_EXCEPTION(HttpStatus.FORBIDDEN, "유효하지 않은 요청입니다."), + /* + 404 - NOT FOUND + */ + STUDY_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 스터디 입니다."), + /* 415 - UNSUPPORTED MEDIA TYPE */ diff --git a/src/main/java/com/stumeet/server/common/response/SuccessCode.java b/src/main/java/com/stumeet/server/common/response/SuccessCode.java index 3db1d9e6..f4179184 100644 --- a/src/main/java/com/stumeet/server/common/response/SuccessCode.java +++ b/src/main/java/com/stumeet/server/common/response/SuccessCode.java @@ -9,6 +9,7 @@ @AllArgsConstructor(access = AccessLevel.PRIVATE) public enum SuccessCode { + GET_SUCCESS(HttpStatus.OK, "조회에 성공했습니다."), SIGN_UP_SUCCESS(HttpStatus.CREATED, "회원가입에 성공했습니다."), FILE_UPLOAD_SUCCESS(HttpStatus.CREATED, "파일 업로드에 성공했습니다.") ; diff --git a/src/main/java/com/stumeet/server/member/adapter/in/web/MemberProfileApi.java b/src/main/java/com/stumeet/server/member/adapter/in/web/MemberProfileApi.java index ee56e226..ac36d990 100644 --- a/src/main/java/com/stumeet/server/member/adapter/in/web/MemberProfileApi.java +++ b/src/main/java/com/stumeet/server/member/adapter/in/web/MemberProfileApi.java @@ -3,6 +3,7 @@ import com.stumeet.server.common.annotation.WebAdapter; import com.stumeet.server.common.auth.model.LoginMember; import com.stumeet.server.common.model.ApiResponse; +import com.stumeet.server.member.adapter.in.web.response.MemberProfileResponse; import com.stumeet.server.member.application.port.in.MemberProfileUseCase; import com.stumeet.server.member.application.port.in.command.MemberUpdateCommand; import jakarta.validation.Valid; @@ -10,6 +11,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -31,4 +33,15 @@ public ResponseEntity> updateMyProfile( HttpStatus.OK ); } + + @GetMapping("/me") + public ResponseEntity> getMyProfile( + @AuthenticationPrincipal LoginMember member + ) { + MemberProfileResponse response = memberProfileUseCase.getProfileById(member.getMember().getId()); + return new ResponseEntity<>( + ApiResponse.success(HttpStatus.OK.value(), "내 프로필 조회에 성공했습니다.", response), + HttpStatus.OK + ); + } } diff --git a/src/main/java/com/stumeet/server/member/adapter/in/web/response/MemberProfileResponse.java b/src/main/java/com/stumeet/server/member/adapter/in/web/response/MemberProfileResponse.java new file mode 100644 index 00000000..d853d54d --- /dev/null +++ b/src/main/java/com/stumeet/server/member/adapter/in/web/response/MemberProfileResponse.java @@ -0,0 +1,15 @@ +package com.stumeet.server.member.adapter.in.web.response; + +import lombok.Builder; + +@Builder +public record MemberProfileResponse( + Long id, + String image, + String nickname, + String region, + String profession, + String tier, + double experience +) { +} diff --git a/src/main/java/com/stumeet/server/member/adapter/out/persistence/MemberJpaEntity.java b/src/main/java/com/stumeet/server/member/adapter/out/persistence/MemberJpaEntity.java index e55fa4c3..3a040646 100644 --- a/src/main/java/com/stumeet/server/member/adapter/out/persistence/MemberJpaEntity.java +++ b/src/main/java/com/stumeet/server/member/adapter/out/persistence/MemberJpaEntity.java @@ -2,7 +2,7 @@ import com.stumeet.server.common.model.BaseTimeEntity; import com.stumeet.server.member.domain.AuthType; -import com.stumeet.server.member.domain.MemberRank; +import com.stumeet.server.member.domain.MemberTier; import com.stumeet.server.member.domain.UserRole; import com.stumeet.server.profession.adapter.out.persistence.ProfessionJpaEntity; import jakarta.persistence.*; @@ -37,10 +37,10 @@ public class MemberJpaEntity extends BaseTimeEntity { @Comment("멤버 이미지 URL") private String image; - @Column(name = "rank", length = 50, nullable = false) + @Column(name = "tier", length = 50, nullable = false) @Enumerated(EnumType.STRING) @Comment("등급") - private MemberRank rank; + private MemberTier tier; @Column(name = "experience", nullable = false) @Comment("경험치") diff --git a/src/main/java/com/stumeet/server/member/adapter/out/persistence/MemberPersistenceMapper.java b/src/main/java/com/stumeet/server/member/adapter/out/persistence/MemberPersistenceMapper.java index bfc5899e..3c64a78b 100644 --- a/src/main/java/com/stumeet/server/member/adapter/out/persistence/MemberPersistenceMapper.java +++ b/src/main/java/com/stumeet/server/member/adapter/out/persistence/MemberPersistenceMapper.java @@ -17,7 +17,7 @@ public MemberJpaEntity toEntity(Member domain) { .id(domain.getId()) .name(domain.getName()) .image(domain.getImage()) - .rank(domain.getLevel().getRank()) + .tier(domain.getLevel().getTier()) .experience(domain.getLevel().getExperience()) .region(domain.getRegion()) .profession(professionPersistenceMapper.toEntity(domain.getProfession())) @@ -28,7 +28,7 @@ public MemberJpaEntity toEntity(Member domain) { public Member toDomain(MemberJpaEntity entity) { MemberLevel level = MemberLevel.builder() - .rank(entity.getRank()) + .tier(entity.getTier()) .experience(entity.getExperience()) .build(); diff --git a/src/main/java/com/stumeet/server/member/application/port/in/MemberProfileUseCase.java b/src/main/java/com/stumeet/server/member/application/port/in/MemberProfileUseCase.java index 821dc99a..73d710ea 100644 --- a/src/main/java/com/stumeet/server/member/application/port/in/MemberProfileUseCase.java +++ b/src/main/java/com/stumeet/server/member/application/port/in/MemberProfileUseCase.java @@ -1,5 +1,6 @@ package com.stumeet.server.member.application.port.in; +import com.stumeet.server.member.adapter.in.web.response.MemberProfileResponse; import com.stumeet.server.member.application.port.in.command.MemberSignupCommand; import com.stumeet.server.member.application.port.in.command.MemberUpdateCommand; import com.stumeet.server.member.domain.Member; @@ -8,4 +9,6 @@ public interface MemberProfileUseCase { void signup(Member member, MemberSignupCommand request); void updateProfile(Member member, MemberUpdateCommand request); + + MemberProfileResponse getProfileById(Long id); } diff --git a/src/main/java/com/stumeet/server/member/application/port/in/mapper/MemberUseCaseMapper.java b/src/main/java/com/stumeet/server/member/application/port/in/mapper/MemberUseCaseMapper.java new file mode 100644 index 00000000..327b2637 --- /dev/null +++ b/src/main/java/com/stumeet/server/member/application/port/in/mapper/MemberUseCaseMapper.java @@ -0,0 +1,21 @@ +package com.stumeet.server.member.application.port.in.mapper; + +import com.stumeet.server.member.adapter.in.web.response.MemberProfileResponse; +import com.stumeet.server.member.domain.Member; +import org.springframework.stereotype.Component; + +@Component +public class MemberUseCaseMapper { + + public MemberProfileResponse toProfileResponse(Member member) { + return MemberProfileResponse.builder() + .id(member.getId()) + .image(member.getImage()) + .nickname(member.getName()) + .region(member.getRegion()) + .profession(member.getProfession().getName()) + .tier(member.getLevel().getTier().getName()) + .experience(member.getLevel().getExperience()) + .build(); + } +} diff --git a/src/main/java/com/stumeet/server/member/application/service/MemberOAuthService.java b/src/main/java/com/stumeet/server/member/application/service/MemberOAuthService.java index 20133cd8..e3b11036 100644 --- a/src/main/java/com/stumeet/server/member/application/service/MemberOAuthService.java +++ b/src/main/java/com/stumeet/server/member/application/service/MemberOAuthService.java @@ -29,7 +29,7 @@ public Member getMemberOrCreate(OAuthUserProfileResponse response, String provid member = memberQueryPort.getByOAuthProviderId(response.id(), oAuthProvider); } else { MemberLevel initialLevel = MemberLevel.builder() - .rank(MemberRank.SEED) + .tier(MemberTier.SEED) .experience(0.0) .build(); member = memberCommandPort.save( diff --git a/src/main/java/com/stumeet/server/member/application/service/MemberProfileService.java b/src/main/java/com/stumeet/server/member/application/service/MemberProfileService.java index b1e214e8..4df75290 100644 --- a/src/main/java/com/stumeet/server/member/application/service/MemberProfileService.java +++ b/src/main/java/com/stumeet/server/member/application/service/MemberProfileService.java @@ -2,11 +2,14 @@ import com.stumeet.server.common.annotation.UseCase; import com.stumeet.server.file.application.port.in.FileUploadUseCase; +import com.stumeet.server.member.adapter.in.web.response.MemberProfileResponse; import com.stumeet.server.member.application.port.in.MemberProfileUseCase; import com.stumeet.server.member.application.port.in.command.MemberProfileCommand; import com.stumeet.server.member.application.port.in.command.MemberSignupCommand; import com.stumeet.server.member.application.port.in.command.MemberUpdateCommand; +import com.stumeet.server.member.application.port.in.mapper.MemberUseCaseMapper; import com.stumeet.server.member.application.port.out.MemberCommandPort; +import com.stumeet.server.member.application.port.out.MemberQueryPort; import com.stumeet.server.member.domain.Member; import com.stumeet.server.profession.application.port.in.ProfessionQueryUseCase; import com.stumeet.server.profession.domain.Profession; @@ -21,6 +24,9 @@ public class MemberProfileService implements MemberProfileUseCase { private final ProfessionQueryUseCase professionQueryUseCase; private final FileUploadUseCase fileUploadUseCase; private final MemberCommandPort memberCommandPort; + private final MemberQueryPort memberQueryPort; + private final MemberUseCaseMapper memberUseCaseMapper; + @Override public void signup(Member member, MemberSignupCommand request) { @@ -61,4 +67,10 @@ public void updateProfile(Member member, MemberUpdateCommand request) { memberCommandPort.update(member); } + @Override + public MemberProfileResponse getProfileById(Long id) { + Member member = memberQueryPort.getById(id); + return memberUseCaseMapper.toProfileResponse(member); + } + } diff --git a/src/main/java/com/stumeet/server/member/domain/MemberLevel.java b/src/main/java/com/stumeet/server/member/domain/MemberLevel.java index 4012f013..ecaee591 100644 --- a/src/main/java/com/stumeet/server/member/domain/MemberLevel.java +++ b/src/main/java/com/stumeet/server/member/domain/MemberLevel.java @@ -9,6 +9,6 @@ @Builder public class MemberLevel { - private MemberRank rank; + private MemberTier tier; private double experience; } diff --git a/src/main/java/com/stumeet/server/member/domain/MemberRank.java b/src/main/java/com/stumeet/server/member/domain/MemberTier.java similarity index 92% rename from src/main/java/com/stumeet/server/member/domain/MemberRank.java rename to src/main/java/com/stumeet/server/member/domain/MemberTier.java index 5213b005..b6615e21 100644 --- a/src/main/java/com/stumeet/server/member/domain/MemberRank.java +++ b/src/main/java/com/stumeet/server/member/domain/MemberTier.java @@ -5,7 +5,7 @@ @RequiredArgsConstructor @Getter -public enum MemberRank { +public enum MemberTier { SEED("씨앗"), SPROUT("새싹"), LEAF("잎"), diff --git a/src/main/java/com/stumeet/server/study/adapter/in/web/StudyQueryApi.java b/src/main/java/com/stumeet/server/study/adapter/in/web/StudyQueryApi.java new file mode 100644 index 00000000..cd239f72 --- /dev/null +++ b/src/main/java/com/stumeet/server/study/adapter/in/web/StudyQueryApi.java @@ -0,0 +1,30 @@ +package com.stumeet.server.study.adapter.in.web; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; + +import com.stumeet.server.common.annotation.WebAdapter; +import com.stumeet.server.common.model.ApiResponse; +import com.stumeet.server.common.response.SuccessCode; +import com.stumeet.server.study.adapter.in.web.response.StudyDetailResponse; +import com.stumeet.server.study.application.port.in.StudyQueryUseCase; + +import lombok.RequiredArgsConstructor; + +@WebAdapter +@RequestMapping("/api/v1/studies") +@RequiredArgsConstructor +public class StudyQueryApi { + + private final StudyQueryUseCase studyQueryUseCase; + + @GetMapping("/{id}") + public ResponseEntity> getStudy( + @PathVariable(name = "id") Long id + ) { + StudyDetailResponse response = studyQueryUseCase.getStudyDetailById(id); + return ResponseEntity.ok(ApiResponse.success(SuccessCode.GET_SUCCESS, response)); + } +} diff --git a/src/main/java/com/stumeet/server/study/adapter/in/web/response/StudyDetailResponse.java b/src/main/java/com/stumeet/server/study/adapter/in/web/response/StudyDetailResponse.java new file mode 100644 index 00000000..5b1e480b --- /dev/null +++ b/src/main/java/com/stumeet/server/study/adapter/in/web/response/StudyDetailResponse.java @@ -0,0 +1,24 @@ +package com.stumeet.server.study.adapter.in.web.response; + +import java.time.LocalDateTime; +import java.util.List; + +import lombok.Builder; + +@Builder +public record StudyDetailResponse( + Long id, + String field, + String name, + List topics, + String intro, + String region, + String rule, + String mainImage, + int headCount, + LocalDateTime startDate, + LocalDateTime endDate, + boolean isFinished, + boolean isDeleted +) { +} diff --git a/src/main/java/com/stumeet/server/study/adapter/out/persistance/JpaStudyRepository.java b/src/main/java/com/stumeet/server/study/adapter/out/persistance/JpaStudyRepository.java new file mode 100644 index 00000000..02207a99 --- /dev/null +++ b/src/main/java/com/stumeet/server/study/adapter/out/persistance/JpaStudyRepository.java @@ -0,0 +1,8 @@ +package com.stumeet.server.study.adapter.out.persistance; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.stumeet.server.study.adapter.out.persistance.entity.StudyJpaEntity; + +public interface JpaStudyRepository extends JpaRepository { +} diff --git a/src/main/java/com/stumeet/server/study/adapter/out/persistance/StudyPersistenceAdapter.java b/src/main/java/com/stumeet/server/study/adapter/out/persistance/StudyPersistenceAdapter.java new file mode 100644 index 00000000..f404ea27 --- /dev/null +++ b/src/main/java/com/stumeet/server/study/adapter/out/persistance/StudyPersistenceAdapter.java @@ -0,0 +1,23 @@ +package com.stumeet.server.study.adapter.out.persistance; + +import com.stumeet.server.common.annotation.PersistenceAdapter; +import com.stumeet.server.study.adapter.out.persistance.entity.StudyJpaEntity; +import com.stumeet.server.study.adapter.out.persistance.mapper.StudyPersistenceMapper; +import com.stumeet.server.study.application.port.out.StudyQueryPort; +import com.stumeet.server.study.domain.Study; + +import lombok.RequiredArgsConstructor; + +@PersistenceAdapter +@RequiredArgsConstructor +public class StudyPersistenceAdapter implements StudyQueryPort { + + private final StudyRepository studyRepository; + private final StudyPersistenceMapper studyPersistenceMapper; + + @Override + public Study getById(Long id) { + StudyJpaEntity entity = studyRepository.findById(id); + return studyPersistenceMapper.toDomain(entity); + } +} diff --git a/src/main/java/com/stumeet/server/study/adapter/out/persistance/StudyRepository.java b/src/main/java/com/stumeet/server/study/adapter/out/persistance/StudyRepository.java new file mode 100644 index 00000000..b139fbb7 --- /dev/null +++ b/src/main/java/com/stumeet/server/study/adapter/out/persistance/StudyRepository.java @@ -0,0 +1,9 @@ +package com.stumeet.server.study.adapter.out.persistance; + +import com.stumeet.server.study.adapter.out.persistance.entity.StudyJpaEntity; + +public interface StudyRepository { + + StudyJpaEntity findById(Long id); + +} diff --git a/src/main/java/com/stumeet/server/study/adapter/out/persistance/StudyRepositoryImpl.java b/src/main/java/com/stumeet/server/study/adapter/out/persistance/StudyRepositoryImpl.java new file mode 100644 index 00000000..4ea34c28 --- /dev/null +++ b/src/main/java/com/stumeet/server/study/adapter/out/persistance/StudyRepositoryImpl.java @@ -0,0 +1,22 @@ +package com.stumeet.server.study.adapter.out.persistance; + +import org.springframework.stereotype.Repository; + +import com.stumeet.server.common.exception.model.BusinessException; +import com.stumeet.server.common.response.ErrorCode; +import com.stumeet.server.study.adapter.out.persistance.entity.StudyJpaEntity; + +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class StudyRepositoryImpl implements StudyRepository { + + private final JpaStudyRepository jpaStudyRepository; + + @Override + public StudyJpaEntity findById(Long id) { + return jpaStudyRepository.findById(id) + .orElseThrow(() -> new BusinessException(ErrorCode.STUDY_NOT_FOUND)); + } +} diff --git a/src/main/java/com/stumeet/server/study/adapter/out/persistance/entity/StudyFieldJpaEntity.java b/src/main/java/com/stumeet/server/study/adapter/out/persistance/entity/StudyFieldJpaEntity.java new file mode 100644 index 00000000..39ef9154 --- /dev/null +++ b/src/main/java/com/stumeet/server/study/adapter/out/persistance/entity/StudyFieldJpaEntity.java @@ -0,0 +1,33 @@ +package com.stumeet.server.study.adapter.out.persistance.entity; + +import org.hibernate.annotations.Comment; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Table(name = "study_field") +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class StudyFieldJpaEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Comment("스터디 분야 아이디") + private Long id; + + @Column(name = "name", length = 20) + @Comment("분야명") + private String name; +} diff --git a/src/main/java/com/stumeet/server/study/adapter/out/persistance/entity/StudyJpaEntity.java b/src/main/java/com/stumeet/server/study/adapter/out/persistance/entity/StudyJpaEntity.java new file mode 100644 index 00000000..36e901d4 --- /dev/null +++ b/src/main/java/com/stumeet/server/study/adapter/out/persistance/entity/StudyJpaEntity.java @@ -0,0 +1,100 @@ +package com.stumeet.server.study.adapter.out.persistance.entity; + +import java.time.LocalDateTime; + +import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.Comment; + +import com.stumeet.server.common.model.BaseTimeEntity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Table(name = "study") +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class StudyJpaEntity extends BaseTimeEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Comment("스터디 그룹 아이디") + private Long id; + + @ManyToOne + @JoinColumn(name = "study_field_id", nullable = false) + @Comment("분야") + private StudyFieldJpaEntity studyField; + + @Column(name = "name", length = 20, nullable = false) + @Comment("스터디명") + private String name; + + @Column(name = "topics") + @Comment("주제") + private String topics; + + @Column(name = "intro", length = 100, nullable = false) + @Comment("소개") + private String intro; + + @Column(name = "region", length = 50, nullable = false) + @Comment("지역") + private String region; + + @Column(name = "rule", length = 100) + @Comment("규칙") + private String rule; + + @Column(name = "main_image", length = 500) + @Comment("메인 이미지") + private String mainImage; + + @Column(name = "head_count", length = 100, nullable = false) + @ColumnDefault("1") + @Comment("현재 인원") + private int headCount; + + @Column(name = "start_date", nullable = false) + @Comment("시작일") + private LocalDateTime startDate; + + @Column(name = "end_date", nullable = false) + @Comment("종료일") + private LocalDateTime endDate; + + @Column(name = "is_finished", nullable = false) + @ColumnDefault("false") + @Comment("완료 여부") + private boolean isFinished; + + @Column(name = "is_deleted", nullable = false) + @ColumnDefault("false") + @Comment("삭제 여부") + private boolean isDeleted; + + @Column(name = "deleted_at") + @Comment("삭제된 시간") + private LocalDateTime deletedAt; + + public Boolean getIsFinished() { + return this.isFinished; + } + + public Boolean getIsDeleted() { + return this.isDeleted; + } +} diff --git a/src/main/java/com/stumeet/server/study/adapter/out/persistance/mapper/StudyFieldPersistenceMapper.java b/src/main/java/com/stumeet/server/study/adapter/out/persistance/mapper/StudyFieldPersistenceMapper.java new file mode 100644 index 00000000..ae4ce012 --- /dev/null +++ b/src/main/java/com/stumeet/server/study/adapter/out/persistance/mapper/StudyFieldPersistenceMapper.java @@ -0,0 +1,24 @@ +package com.stumeet.server.study.adapter.out.persistance.mapper; + +import org.springframework.stereotype.Component; + +import com.stumeet.server.study.adapter.out.persistance.entity.StudyFieldJpaEntity; +import com.stumeet.server.study.domain.StudyField; + +@Component +public class StudyFieldPersistenceMapper { + + public StudyFieldJpaEntity toEntity(StudyField domain) { + return StudyFieldJpaEntity.builder() + .id(domain.getId()) + .name(domain.getName()) + .build(); + } + + public StudyField toDomain(StudyFieldJpaEntity entity) { + return StudyField.builder() + .id(entity.getId()) + .name(entity.getName()) + .build(); + } +} diff --git a/src/main/java/com/stumeet/server/study/adapter/out/persistance/mapper/StudyPersistenceMapper.java b/src/main/java/com/stumeet/server/study/adapter/out/persistance/mapper/StudyPersistenceMapper.java new file mode 100644 index 00000000..3cb456e2 --- /dev/null +++ b/src/main/java/com/stumeet/server/study/adapter/out/persistance/mapper/StudyPersistenceMapper.java @@ -0,0 +1,53 @@ +package com.stumeet.server.study.adapter.out.persistance.mapper; + +import org.springframework.stereotype.Component; + +import com.stumeet.server.study.adapter.out.persistance.entity.StudyJpaEntity; +import com.stumeet.server.study.domain.Study; +import com.stumeet.server.study.domain.StudyHeadCount; +import com.stumeet.server.study.domain.StudyPeriod; +import com.stumeet.server.study.domain.StudyTopics; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class StudyPersistenceMapper { + + private final StudyFieldPersistenceMapper studyFieldPersistenceMapper; + + public StudyJpaEntity toEntity(Study domain) { + return StudyJpaEntity.builder() + .id(domain.getId()) + .name(domain.getName()) + .studyField(studyFieldPersistenceMapper.toEntity(domain.getStudyField())) + .topics(domain.getAssembledTopics()) + .region(domain.getRegion()) + .intro(domain.getIntro()) + .rule(domain.getRule()) + .startDate(domain.getStartDate()) + .endDate(domain.getEndDate()) + .headCount(domain.getHeadCountNumber()) + .mainImage(domain.getMainImage()) + .isFinished(domain.getIsFinished()) + .isDeleted(domain.getIsDeleted()) + .build(); + } + + public Study toDomain(StudyJpaEntity entity) { + return Study.builder() + .id(entity.getId()) + .name(entity.getName()) + .studyField(studyFieldPersistenceMapper.toDomain(entity.getStudyField())) + .studyTopics(StudyTopics.from(entity.getTopics())) + .region(entity.getRegion()) + .intro(entity.getIntro()) + .rule(entity.getRule()) + .period(StudyPeriod.of(entity.getStartDate(), entity.getEndDate())) + .headCount(StudyHeadCount.from(entity.getHeadCount())) + .mainImage(entity.getMainImage()) + .isFinished(entity.getIsFinished()) + .isDeleted(entity.getIsDeleted()) + .build(); + } +} diff --git a/src/main/java/com/stumeet/server/study/application/port/in/StudyQueryUseCase.java b/src/main/java/com/stumeet/server/study/application/port/in/StudyQueryUseCase.java new file mode 100644 index 00000000..b2972b0f --- /dev/null +++ b/src/main/java/com/stumeet/server/study/application/port/in/StudyQueryUseCase.java @@ -0,0 +1,8 @@ +package com.stumeet.server.study.application.port.in; + +import com.stumeet.server.study.adapter.in.web.response.StudyDetailResponse; + +public interface StudyQueryUseCase { + + StudyDetailResponse getStudyDetailById(Long id); +} diff --git a/src/main/java/com/stumeet/server/study/application/port/out/StudyQueryPort.java b/src/main/java/com/stumeet/server/study/application/port/out/StudyQueryPort.java new file mode 100644 index 00000000..98b4d6b9 --- /dev/null +++ b/src/main/java/com/stumeet/server/study/application/port/out/StudyQueryPort.java @@ -0,0 +1,8 @@ +package com.stumeet.server.study.application.port.out; + +import com.stumeet.server.study.domain.Study; + +public interface StudyQueryPort { + + Study getById(Long id); +} diff --git a/src/main/java/com/stumeet/server/study/application/port/out/mapper/StudyUseCaseMapper.java b/src/main/java/com/stumeet/server/study/application/port/out/mapper/StudyUseCaseMapper.java new file mode 100644 index 00000000..d8d55e09 --- /dev/null +++ b/src/main/java/com/stumeet/server/study/application/port/out/mapper/StudyUseCaseMapper.java @@ -0,0 +1,28 @@ +package com.stumeet.server.study.application.port.out.mapper; + +import org.springframework.stereotype.Component; + +import com.stumeet.server.study.adapter.in.web.response.StudyDetailResponse; +import com.stumeet.server.study.domain.Study; + +@Component +public class StudyUseCaseMapper { + + public StudyDetailResponse toStudyDetailResponse(Study study) { + return StudyDetailResponse.builder() + .id(study.getId()) + .name(study.getName()) + .field(study.getStudyFieldName()) + .topics(study.getStudyTopics().getTopics()) + .region(study.getRegion()) + .intro(study.getIntro()) + .rule(study.getRule()) + .startDate(study.getStartDate()) + .endDate(study.getEndDate()) + .headCount(study.getHeadCountNumber()) + .mainImage(study.getMainImage()) + .isFinished(study.getIsFinished()) + .isDeleted(study.getIsDeleted()) + .build(); + } +} diff --git a/src/main/java/com/stumeet/server/study/application/service/StudyQueryService.java b/src/main/java/com/stumeet/server/study/application/service/StudyQueryService.java new file mode 100644 index 00000000..d39da283 --- /dev/null +++ b/src/main/java/com/stumeet/server/study/application/service/StudyQueryService.java @@ -0,0 +1,24 @@ +package com.stumeet.server.study.application.service; + +import com.stumeet.server.common.annotation.UseCase; +import com.stumeet.server.study.adapter.in.web.response.StudyDetailResponse; +import com.stumeet.server.study.application.port.in.StudyQueryUseCase; +import com.stumeet.server.study.application.port.out.StudyQueryPort; +import com.stumeet.server.study.application.port.out.mapper.StudyUseCaseMapper; +import com.stumeet.server.study.domain.Study; + +import lombok.RequiredArgsConstructor; + +@UseCase +@RequiredArgsConstructor +public class StudyQueryService implements StudyQueryUseCase { + + private final StudyQueryPort studyQueryPort; + private final StudyUseCaseMapper studyUseCaseMapper; + + @Override + public StudyDetailResponse getStudyDetailById(Long id) { + Study study = studyQueryPort.getById(id); + return studyUseCaseMapper.toStudyDetailResponse(study); + } +} diff --git a/src/main/java/com/stumeet/server/study/domain/Study.java b/src/main/java/com/stumeet/server/study/domain/Study.java new file mode 100644 index 00000000..a3f59542 --- /dev/null +++ b/src/main/java/com/stumeet/server/study/domain/Study.java @@ -0,0 +1,59 @@ +package com.stumeet.server.study.domain; + +import java.time.LocalDateTime; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Builder +@Getter +public class Study { + + private Long id; + + private String name; + + private StudyField studyField; + + private StudyTopics studyTopics; + + private String region; + + private String intro; + + private String rule; + + private StudyPeriod period; + + private StudyHeadCount headCount; + + private String mainImage; + + private Boolean isFinished; + + private Boolean isDeleted; + + public String getStudyFieldName() { + return studyField.getName(); + } + + public String getAssembledTopics() { + return studyTopics.assemble(); + } + + public int getHeadCountNumber() { + return headCount.getNumber(); + } + + public LocalDateTime getStartDate() { + return period.getStartDate(); + } + + public LocalDateTime getEndDate() { + return period.getEndDate(); + } +} + diff --git a/src/main/java/com/stumeet/server/study/domain/StudyField.java b/src/main/java/com/stumeet/server/study/domain/StudyField.java new file mode 100644 index 00000000..7cb01c6d --- /dev/null +++ b/src/main/java/com/stumeet/server/study/domain/StudyField.java @@ -0,0 +1,16 @@ +package com.stumeet.server.study.domain; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Builder +@Getter +public class StudyField { + + private Long id; + + private String name; +} diff --git a/src/main/java/com/stumeet/server/study/domain/StudyHeadCount.java b/src/main/java/com/stumeet/server/study/domain/StudyHeadCount.java new file mode 100644 index 00000000..b17d4e07 --- /dev/null +++ b/src/main/java/com/stumeet/server/study/domain/StudyHeadCount.java @@ -0,0 +1,17 @@ +package com.stumeet.server.study.domain; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +public class StudyHeadCount { + + private int number; + + public static StudyHeadCount from(int number) { + return new StudyHeadCount(number); + } +} + diff --git a/src/main/java/com/stumeet/server/study/domain/StudyPeriod.java b/src/main/java/com/stumeet/server/study/domain/StudyPeriod.java new file mode 100644 index 00000000..82e87774 --- /dev/null +++ b/src/main/java/com/stumeet/server/study/domain/StudyPeriod.java @@ -0,0 +1,20 @@ +package com.stumeet.server.study.domain; + +import java.time.LocalDateTime; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@Getter +public class StudyPeriod { + + private LocalDateTime startDate; + + private LocalDateTime endDate; + + public static StudyPeriod of(LocalDateTime startDate, LocalDateTime endDate) { + return new StudyPeriod(startDate, endDate); + } +} diff --git a/src/main/java/com/stumeet/server/study/domain/StudyTopics.java b/src/main/java/com/stumeet/server/study/domain/StudyTopics.java new file mode 100644 index 00000000..34b3369d --- /dev/null +++ b/src/main/java/com/stumeet/server/study/domain/StudyTopics.java @@ -0,0 +1,35 @@ +package com.stumeet.server.study.domain; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import lombok.Getter; + +@Getter +public class StudyTopics { + + private static final String TOPIC_DELIMITER = ";"; + + private final List topics; + + private StudyTopics(List topics) { + this.topics = topics; + } + + public static StudyTopics from(String rawTopics) { + List topics = splitTopicsIntoList(rawTopics); + return new StudyTopics(topics); + } + + private static List splitTopicsIntoList(String rawTopics) { + if (rawTopics != null) { + return Arrays.stream(rawTopics.split(TOPIC_DELIMITER)).toList(); + } + return Collections.emptyList(); + } + + protected String assemble() { + return String.join(";", topics); + } +} diff --git a/src/main/resources/db/migration/V1.3__modify_column_rank_to_tier.sql b/src/main/resources/db/migration/V1.3__modify_column_rank_to_tier.sql new file mode 100644 index 00000000..2086f612 --- /dev/null +++ b/src/main/resources/db/migration/V1.3__modify_column_rank_to_tier.sql @@ -0,0 +1 @@ +ALTER TABLE member RENAME COLUMN `rank` TO `tier` \ No newline at end of file diff --git a/src/main/resources/db/migration/V1.4__add_study_table.sql b/src/main/resources/db/migration/V1.4__add_study_table.sql new file mode 100644 index 00000000..1a8a7b20 --- /dev/null +++ b/src/main/resources/db/migration/V1.4__add_study_table.sql @@ -0,0 +1,33 @@ + CREATE TABLE `study_field` + ( + `id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '스터디 분야 ID', + `name` VARCHAR(50) NOT NULL COMMENT '분야명' + ) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci; + + CREATE TABLE `study` + ( + `id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '스터디 ID', + `study_field_id` BIGINT NULL COMMENT '스터디 분야 ID', + `name` VARCHAR(255) NOT NULL COMMENT '스터디명', + `region` VARCHAR(50) NOT NULL COMMENT '활동 지역', + `topic` VARCHAR(100) NULL COMMENT '주제', + `intro` VARCHAR(100) NOT NULL COMMENT '소개', + `rule` VARCHAR(100) NULL COMMENT '규칙', + `image` VARCHAR(500) NOT NULL COMMENT '스터디 이미지의 URL', + `headcount` TINYINT(50) NOT NULL DEFAULT 1 COMMENT '인원 수', + `start_date` DATETIME NOT NULL COMMENT '시작일', + `end_date` DATETIME NOT NULL COMMENT '종료일', + `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '생성 시간', + `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정 시간', + `is_finished` TINYINT(1) NOT NULL DEFAULT FALSE COMMENT '스터디 완료 여부', + `is_deleted` TINYINT(1) NOT NULL DEFAULT FALSE COMMENT '스터디 삭제 여부', + `deleted_at` DATETIME NULL COMMENT '스터디 삭제 일자', + + CONSTRAINT fk_study_field FOREIGN KEY (study_field_id) + REFERENCES study_field (id) + ON DELETE SET NULL ON UPDATE CASCADE + ) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE = utf8mb4_unicode_ci; \ No newline at end of file diff --git a/src/main/resources/db/migration/V1.5_insert_dummy_study_data.sql b/src/main/resources/db/migration/V1.5_insert_dummy_study_data.sql new file mode 100644 index 00000000..baef8555 --- /dev/null +++ b/src/main/resources/db/migration/V1.5_insert_dummy_study_data.sql @@ -0,0 +1,7 @@ +insert into study_field value (1, 'IT'); + +INSERT INTO study (study_field_id, name, region, topic, intro, rule, image, start_date, end_date) +VALUES (1, '[임시] 프로그래밍 스터디', '서울', 'java;springboot;프로그래밍;', + '프로그래밍 스터디 입니다.', '- 매주 목요일 8시\n- 장소: 안암역\n- 제시간에 제출하기!', + 'https://stumeet.s3.ap-northeast-2.amazonaws.com/study/1/mainImage/2023062711172178420.png', + '2024-04-01', '2024-05-01'); \ No newline at end of file diff --git a/src/test/java/com/stumeet/server/common/auth/filter/LogoutFilterTest.java b/src/test/java/com/stumeet/server/common/auth/filter/LogoutFilterTest.java index 293c793a..38267141 100644 --- a/src/test/java/com/stumeet/server/common/auth/filter/LogoutFilterTest.java +++ b/src/test/java/com/stumeet/server/common/auth/filter/LogoutFilterTest.java @@ -29,7 +29,6 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@Transactional public class LogoutFilterTest extends ApiTest { @Container @@ -54,11 +53,10 @@ class Logout { @BeforeEach void setUp() { - MemberJpaEntity entity = jpaMemberRepository.save(MemberStub.getMemberEntity()); redisTemplate.opsForValue() .set( JwtUtil.resolveToken(TokenStub.getMockAccessToken()), - jwtTokenProvider.generateRefreshToken(entity.getId()) + jwtTokenProvider.generateRefreshToken(MemberStub.getMember().getId()) ); } diff --git a/src/test/java/com/stumeet/server/common/auth/filter/OAuthAuthenticationFilterTest.java b/src/test/java/com/stumeet/server/common/auth/filter/OAuthAuthenticationFilterTest.java index 161ac529..fa4f7d63 100644 --- a/src/test/java/com/stumeet/server/common/auth/filter/OAuthAuthenticationFilterTest.java +++ b/src/test/java/com/stumeet/server/common/auth/filter/OAuthAuthenticationFilterTest.java @@ -13,7 +13,6 @@ import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.http.HttpStatus; import org.springframework.restdocs.payload.JsonFieldType; -import org.springframework.transaction.annotation.Transactional; import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; @@ -29,7 +28,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @WireMockTest(httpPort = 8089) -@Transactional class OAuthAuthenticationFilterTest extends ApiTest { @Container @@ -42,6 +40,7 @@ class OAuthAuthenticationFilterTest extends ApiTest { class oauthLogin { private final String path = "/api/v1/oauth"; + @Test @DisplayName("[성공] 소셜 로그인(카카오)에 성공합니다.") void successKakaoTest() throws Exception { @@ -75,7 +74,7 @@ void invalidAccessTokenTest() throws Exception { mockFailKakaoTokenInfoApi(); mockMvc.perform(post(path) - .header(AuthenticationHeader.ACCESS_TOKEN.getName(), TokenStub.getKakaoAccessToken()) + .header(AuthenticationHeader.ACCESS_TOKEN.getName(), TokenStub.getInvalidToken()) .header(AuthenticationHeader.X_OAUTH_PROVIDER.getName(), OAuthProvider.KAKAO.getProvider())) .andExpect(status().isUnauthorized()) .andDo(document("social_login/fail/invalid-token", @@ -109,10 +108,34 @@ void notExistHeaderTest() throws Exception { ); } + @Test + @DisplayName("[실패] 잘못 서명된 토큰을 전달하는 경우 인증에 실패합니다.") + void invalidSignatureTest() throws Exception { + mockFailAppleInvalidSignatureTokenInfoApi(); + + mockMvc.perform(post(path) + .header(AuthenticationHeader.ACCESS_TOKEN.getName(), TokenStub.getInvalidSignatureAccessToken()) + .header(AuthenticationHeader.X_OAUTH_PROVIDER.getName(), OAuthProvider.APPLE.getProvider())) + .andExpect(status().isUnauthorized()) + .andDo(document("social_login/fail/invalid-signature", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName(AuthenticationHeader.ACCESS_TOKEN.getName()).description("OAuth Provider로 부터 전달받은 토큰"), + headerWithName(AuthenticationHeader.X_OAUTH_PROVIDER.getName()).description("OAuth Provider 이름") + ), + responseFields( + fieldWithPath("code").type(JsonFieldType.NUMBER).description("응답에 대한 결과 코드"), + fieldWithPath("message").type(JsonFieldType.STRING).description("응답에 대한 메시지") + ) + ) + ); + } + private void mockFailKakaoTokenInfoApi() { stubFor( WireMock.get(WireMock.urlEqualTo("/v1/user/access_token_info")) - .withHeader(AuthenticationHeader.ACCESS_TOKEN.getName(), equalTo(TokenStub.getInvalidKakaoAccessToken())) + .withHeader(AuthenticationHeader.ACCESS_TOKEN.getName(), equalTo(TokenStub.getInvalidToken())) .willReturn(aResponse() .withStatus(HttpStatus.UNAUTHORIZED.value()) .withHeader("content-type", APPLICATION_JSON) @@ -132,5 +155,16 @@ private void mockSuccessKakaoTokenInfoApi() { ) ); } + + private void mockFailAppleInvalidSignatureTokenInfoApi() { + stubFor( + WireMock.get(WireMock.urlEqualTo("/auth/keys")) + .willReturn(aResponse() + .withStatus(HttpStatus.OK.value()) + .withHeader("content-type", APPLICATION_JSON) + .withBody(MemberStub.getAppleInvalidSignatureTokenInfo()) + ) + ); + } } } \ No newline at end of file diff --git a/src/test/java/com/stumeet/server/member/adapter/in/web/MemberAuthApiTest.java b/src/test/java/com/stumeet/server/member/adapter/in/web/MemberAuthApiTest.java index 21b6f425..f81363f4 100644 --- a/src/test/java/com/stumeet/server/member/adapter/in/web/MemberAuthApiTest.java +++ b/src/test/java/com/stumeet/server/member/adapter/in/web/MemberAuthApiTest.java @@ -34,7 +34,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@Transactional class MemberAuthApiTest extends ApiTest { @Container @@ -61,8 +60,7 @@ class tokenRenew { @BeforeEach void setUp() { - MemberJpaEntity entity = jpaMemberRepository.save(MemberStub.getMemberEntity()); - refreshToken = jwtTokenProvider.generateRefreshToken(entity.getId()); + refreshToken = jwtTokenProvider.generateRefreshToken(MemberStub.getMember().getId()); redisTemplate.opsForValue() .set(TokenStub.getExpiredAccessToken(), refreshToken); } diff --git a/src/test/java/com/stumeet/server/member/adapter/in/web/MemberProfileApiTest.java b/src/test/java/com/stumeet/server/member/adapter/in/web/MemberProfileApiTest.java index f0e38065..97429797 100644 --- a/src/test/java/com/stumeet/server/member/adapter/in/web/MemberProfileApiTest.java +++ b/src/test/java/com/stumeet/server/member/adapter/in/web/MemberProfileApiTest.java @@ -2,23 +2,20 @@ import com.stumeet.server.common.auth.model.AuthenticationHeader; import com.stumeet.server.helper.WithMockMember; -import com.stumeet.server.member.adapter.out.persistence.JpaMemberRepository; import com.stumeet.server.member.application.port.in.command.MemberUpdateCommand; import com.stumeet.server.stub.MemberStub; import com.stumeet.server.stub.TokenStub; import com.stumeet.server.template.ApiTest; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.web.servlet.request.RequestPostProcessor; -import org.springframework.transaction.annotation.Transactional; import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; @@ -26,23 +23,14 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@Transactional class MemberProfileApiTest extends ApiTest { - @Autowired - private JpaMemberRepository memberRepository; - @Nested @DisplayName("내 프로필 수정") class UpdateMyProfile { private final String path = "/api/v1/members/me"; - @BeforeEach - void setUp() { - memberRepository.save(MemberStub.getMemberEntity()); - } - @Test @WithMockMember @DisplayName("[성공] 회원 프로필 수정에 성공한다.") @@ -112,4 +100,33 @@ void invalidRequestTest() throws Exception { ))); } } + + @Nested + @DisplayName("내 프로필 조회") + class GetMyProfile { + + @Test + @WithMockMember + @DisplayName("[성공] 회원 프로필 조회에 성공한다.") + void successTest() throws Exception { + mockMvc.perform(get("/api/v1/members/me") + .header(AuthenticationHeader.ACCESS_TOKEN.getName(), TokenStub.getMockAccessToken())) + .andExpect(status().isOk()) + .andDo(document("get-my-profile/success", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders(headerWithName(AuthenticationHeader.ACCESS_TOKEN.getName()).description("서버로부터 전달받은 액세스 토큰")), + responseFields( + fieldWithPath("code").description("응답 상태"), + fieldWithPath("message").description("응답 메시지"), + fieldWithPath("data.id").description("회원 ID"), + fieldWithPath("data.image").description("프로필 이미지 URL"), + fieldWithPath("data.nickname").description("닉네임"), + fieldWithPath("data.region").description("지역"), + fieldWithPath("data.profession").description("분야 이름"), + fieldWithPath("data.tier").description("회원 레벨 - 랭크"), + fieldWithPath("data.experience").description("회원 레벨 - 경험치") + ))); + } + } } \ No newline at end of file diff --git a/src/test/java/com/stumeet/server/member/adapter/in/web/MemberValidApiTest.java b/src/test/java/com/stumeet/server/member/adapter/in/web/MemberValidApiTest.java index b53f8a2c..d6712605 100644 --- a/src/test/java/com/stumeet/server/member/adapter/in/web/MemberValidApiTest.java +++ b/src/test/java/com/stumeet/server/member/adapter/in/web/MemberValidApiTest.java @@ -26,7 +26,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@Transactional class MemberValidApiTest extends ApiTest { @Autowired @@ -37,12 +36,6 @@ class MemberValidApiTest extends ApiTest { class IsDuplicateNickname { private final String path = "/api/v1/members/validate-nickname"; - private MemberJpaEntity member; - - @BeforeEach - void setUp() { - member = jpaMemberRepository.save(MemberStub.getMemberEntity()); - } @Test @WithMockMember @@ -96,7 +89,7 @@ void invalidRequestTest() throws Exception { @WithMockMember @DisplayName("[실패] 닉네임이 중복되면 검증에 실패합니다.") void duplicateNicknameTest() throws Exception { - String nickname = member.getName(); + String nickname = MemberStub.getMember().getName(); mockMvc.perform(get(path) .header(AuthenticationHeader.ACCESS_TOKEN.getName(), TokenStub.getMockAccessToken()) .param("nickname", nickname)) diff --git a/src/test/java/com/stumeet/server/member/application/service/MemberProfileServiceTest.java b/src/test/java/com/stumeet/server/member/application/service/MemberProfileServiceTest.java index 70d64ee1..58e95e52 100644 --- a/src/test/java/com/stumeet/server/member/application/service/MemberProfileServiceTest.java +++ b/src/test/java/com/stumeet/server/member/application/service/MemberProfileServiceTest.java @@ -1,8 +1,11 @@ package com.stumeet.server.member.application.service; import com.stumeet.server.file.application.port.in.FileUploadUseCase; +import com.stumeet.server.member.adapter.in.web.response.MemberProfileResponse; import com.stumeet.server.member.application.port.in.command.MemberUpdateCommand; +import com.stumeet.server.member.application.port.in.mapper.MemberUseCaseMapper; import com.stumeet.server.member.application.port.out.MemberCommandPort; +import com.stumeet.server.member.application.port.out.MemberQueryPort; import com.stumeet.server.member.domain.Member; import com.stumeet.server.profession.application.port.in.ProfessionQueryUseCase; import com.stumeet.server.stub.FileStub; @@ -15,6 +18,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.then; @@ -33,6 +37,12 @@ class MemberProfileServiceTest extends UnitTest { @Mock private ProfessionQueryUseCase professionQueryUseCase; + @Mock + private MemberQueryPort memberQueryPort; + + @Mock + private MemberUseCaseMapper memberUseCaseMapper; + @Nested @DisplayName("멤버 프로필 업데이트") class UpdateProfile { @@ -54,4 +64,24 @@ void successTest() { } } + @Nested + @DisplayName("프로필 조회") + class GetProfileById { + @Test + @DisplayName("[성공] 멤버 프로필 조회에 성공한다.") + void successTest() { + Member member = MemberStub.getMember(); + MemberProfileResponse want = MemberStub.getMemberProfileResponse(member); + + given(memberQueryPort.getById(member.getId())) + .willReturn(member); + given(memberUseCaseMapper.toProfileResponse(member)) + .willReturn(want); + + MemberProfileResponse got = memberProfileService.getProfileById(member.getId()); + + assertThat(got).usingRecursiveComparison().isEqualTo(want); + } + } + } \ No newline at end of file diff --git a/src/test/java/com/stumeet/server/profession/adapter/in/web/ProfessionQueryApiTest.java b/src/test/java/com/stumeet/server/profession/adapter/in/web/ProfessionQueryApiTest.java index 0bf7a58c..f3f7304f 100644 --- a/src/test/java/com/stumeet/server/profession/adapter/in/web/ProfessionQueryApiTest.java +++ b/src/test/java/com/stumeet/server/profession/adapter/in/web/ProfessionQueryApiTest.java @@ -20,7 +20,6 @@ import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -@Transactional class ProfessionQueryApiTest extends ApiTest { @Nested diff --git a/src/test/java/com/stumeet/server/stub/MemberStub.java b/src/test/java/com/stumeet/server/stub/MemberStub.java index e32215ec..e68525fd 100644 --- a/src/test/java/com/stumeet/server/stub/MemberStub.java +++ b/src/test/java/com/stumeet/server/stub/MemberStub.java @@ -1,10 +1,10 @@ package com.stumeet.server.stub; -import com.stumeet.server.member.adapter.out.persistence.MemberJpaEntity; +import com.stumeet.server.helper.WithMockMember; +import com.stumeet.server.member.adapter.in.web.response.MemberProfileResponse; import com.stumeet.server.member.application.port.in.command.MemberSignupCommand; import com.stumeet.server.member.application.port.in.command.MemberUpdateCommand; import com.stumeet.server.member.domain.*; -import com.stumeet.server.helper.WithMockMember; import org.springframework.mock.web.MockMultipartFile; public class MemberStub { @@ -18,22 +18,10 @@ public static String getKakaoAccessTokenInfo() { } - public static String getInvalidKakaoAccessTokenInfo() { return "{\"msg\":\"this access token does not exist\",\"code\":-401}"; } - public static MemberJpaEntity getMemberEntity() { - return MemberJpaEntity.builder() - .id(1L) - .name("test") - .role(UserRole.FIRST_LOGIN) - .authType(AuthType.OAUTH) - .rank(MemberRank.SEED) - .experience(0.0) - .build(); - } - public static MemberSignupCommand getMemberSignupCommand() { MockMultipartFile image = new MockMultipartFile("image", "test.jpg", "image/jpeg", "test".getBytes()); return new MemberSignupCommand(image, "test", "서울", 1L); @@ -41,7 +29,7 @@ public static MemberSignupCommand getMemberSignupCommand() { public static Member getMember(WithMockMember annotation) { MemberLevel level = MemberLevel.builder() - .rank(MemberRank.SEED) + .tier(MemberTier.SEED) .experience(0.0) .build(); @@ -51,15 +39,15 @@ public static Member getMember(WithMockMember annotation) { .role(annotation.authority()) .authType(AuthType.OAUTH) .level(level) - .profession(null) - .region(null) - .image(null) + .profession(ProfessionStub.getProfession()) + .region("서울") + .image(FileStub.getFileUrl().url()) .build(); } public static Member getMember() { MemberLevel level = MemberLevel.builder() - .rank(MemberRank.SEED) + .tier(MemberTier.SEED) .experience(0.0) .build(); return Member.builder() @@ -68,9 +56,9 @@ public static Member getMember() { .role(UserRole.MEMBER) .authType(AuthType.OAUTH) .level(level) - .profession(null) - .region(null) - .image(null) + .profession(ProfessionStub.getProfession()) + .region("서울") + .image(FileStub.getFileUrl().url()) .build(); } @@ -83,4 +71,20 @@ public static MemberUpdateCommand getInvalidMemberUpdateCommand() { MockMultipartFile invalidImage = new MockMultipartFile("image", "test.jpa", "plain/text", "test".getBytes()); return new MemberUpdateCommand(invalidImage, "닉", " ", -1L); } + + public static MemberProfileResponse getMemberProfileResponse(Member member) { + return MemberProfileResponse.builder() + .id(member.getId()) + .image(member.getImage()) + .nickname(member.getName()) + .region(member.getRegion()) + .profession(member.getProfession().getName()) + .tier(member.getLevel().getTier().getName()) + .experience(member.getLevel().getExperience()) + .build(); + } + + public static String getAppleInvalidSignatureTokenInfo() { + return "{\"keys\":[{\"kty\":\"RSA\",\"kid\":\"lVHdOx8ltR\",\"use\":\"sig\",\"alg\":\"RS256\",\"n\":\"nXDu9MPf6dmVtFbDdAaal_0cO9ur2tqrrmCZaAe8TUWHU8AprhJG4DaQoCIa4UsOSCbCYOjPpPGGdE_p0XeP1ew55pBIquNhNtNNEMX0jNYAKcA9WAP1zGSkvH5m39GMFc4SsGiQ_8Szht9cayJX1SJALEgSyDOFLs-ekHnexqsr-KPtlYciwer5jaNcW3B7f9VNp1XCypQloQwSGVismPHwDJowPQ1xOWmhBLCK50NV38ZjobUDSBbCeLYecMtsdL5ZGv-iufddBh3RHszQiD2G-VXoGOs1yE33K4uAto2F2bHVcKOUy0__9qEsXZGf-B5ZOFucUkoN7T2iqu2E2Q\",\"e\":\"AQAB\"},{\"kty\":\"RSA\",\"kid\":\"pyaRQpAbnY\",\"use\":\"sig\",\"alg\":\"RS256\",\"n\":\"qHiwOpizi6xHG8FIOSWH4l0P1CjLIC7aBFkhbk7BrD4s9KQAs5Sj5xAtOwlZMyP2XFcqRtZBLIMM7vw_CNERtRrhc68se5hQE_vsrHy7ugcQU6ogJS6s54zqO-zTUfaa3mABM6iR-EfgSpvz33WTQZAPtwAyxaSLknHyDzWjHEZ44WqaQBdcMAvgsWMYG5dBfnV-3Or3V2r1vdbinRE5NomE2nsKDbnJ3yo3u-x9TizKazS1JV3umt71xDqbruZLybIrimrzg_i9OSIzT2o5ZWz8zdYkKHZ4cvRPh-DDt8kV7chzR2tenPF2c5WXuK-FumOrjT7WW6uwSvhnhwNZuw\",\"e\":\"AQAB\"},{\"kty\":\"RSA\",\"kid\":\"fh6Bs8C\",\"use\":\"sig\",\"alg\":\"RS256\",\"n\":\"u704gotMSZc6CSSVNCZ1d0S9dZKwO2BVzfdTKYz8wSNm7R_KIufOQf3ru7Pph1FjW6gQ8zgvhnv4IebkGWsZJlodduTC7c0sRb5PZpEyM6PtO8FPHowaracJJsK1f6_rSLstLdWbSDXeSq7vBvDu3Q31RaoV_0YlEzQwPsbCvD45oVy5Vo5oBePUm4cqi6T3cZ-10gr9QJCVwvx7KiQsttp0kUkHM94PlxbG_HAWlEZjvAlxfEDc-_xZQwC6fVjfazs3j1b2DZWsGmBRdx1snO75nM7hpyRRQB4jVejW9TuZDtPtsNadXTr9I5NjxPdIYMORj9XKEh44Z73yfv0gtw\",\"e\":\"AQAB\"},{\"kty\":\"RSA\",\"kid\":\"Bh6H7rHVmb\",\"use\":\"sig\",\"alg\":\"RS256\",\"n\":\"2HkIZ7xKMUYH_wtt2Gwq6jXKRl-Ng5vdwd-XcWn5RIW82-uxdmGJyTo3T6MPty-xWUeW7FCs9NlM4yu02GKgwep7TKfnOovP78sd3rESbZsvuN7zD_Vk6aZP7QfqblElUtiMQxh9bu-gZUeMZfa_ndX-P5C4yKtZvXGrSPLLjyAcSmSHNLZnWbZXjeIVsgXWHMr5fwVEAkftHq_4py82xgn2XEK0FK9HmWOCZ47Wcx9fWBnqSi9JTJTUX0lh-kI5TcYfv9UKX2oe3uyOn-A460E_L_4ximlM-lgi3otw26EZfAGY9FFgSZoACjhgw_z5NRbK9dycHRpeLY9GxIyiYw\",\"e\":\"AQAB\"}]}"; + } } diff --git a/src/test/java/com/stumeet/server/stub/ProfessionStub.java b/src/test/java/com/stumeet/server/stub/ProfessionStub.java index 0c6b53ca..81c3283a 100644 --- a/src/test/java/com/stumeet/server/stub/ProfessionStub.java +++ b/src/test/java/com/stumeet/server/stub/ProfessionStub.java @@ -1,5 +1,6 @@ package com.stumeet.server.stub; +import com.stumeet.server.profession.adapter.out.persistence.ProfessionJpaEntity; import com.stumeet.server.profession.domain.Profession; public class ProfessionStub { @@ -14,4 +15,12 @@ public static Profession getProfession() { .parent(null) .build(); } + + public static ProfessionJpaEntity getProfessionEntity() { + return ProfessionJpaEntity.builder() + .id(1L) + .name("경영사무") + .parent(null) + .build(); + } } diff --git a/src/test/java/com/stumeet/server/stub/TokenStub.java b/src/test/java/com/stumeet/server/stub/TokenStub.java index 48b8fac0..bea20277 100644 --- a/src/test/java/com/stumeet/server/stub/TokenStub.java +++ b/src/test/java/com/stumeet/server/stub/TokenStub.java @@ -2,14 +2,15 @@ public class TokenStub { - private TokenStub() {} + private TokenStub() { + } public static String getKakaoAccessToken() { return "Bearer rjdizj7Ae09H0oWlW46Oll9_x7AhzaJkp1gKKwzTAAABjd_1h0EhI_W2iN1234"; } - public static String getInvalidKakaoAccessToken() { - return "invalidToken"; + public static String getInvalidSignatureAccessToken() { + return "Bearer eyJraWQiOiJweWFSUXBBYm5ZIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiU3R1bWVldC5TdHVtZWV0IiwiZXhwIjoxNzEwMTQzMTM2LCJpYXQiOjE3MTAwNTY3MzYsInN1YiI6IjAwMTY2NS5iODlkNDQxZDYzMGI0Mjk5YTllYjkzNDY2OWFlMjcxMy4xNDE0IiwiY19oYXNoIjoiMlBQN1dpMUdiNzVrVWt3T0Z2YVRVUSIsImVtYWlsIjoidjRoNzRnZDRrakBwcml2YXRlcmVsYXkuYXBwbGVpZC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNfcHJpdmF0ZV9lbWFpbCI6dHJ1ZSwiYXV0aF90aW1lIjoxNzEwMDU2NzM2LCJub25jZV9zdXBwb3J0ZWQiOnRydWV9.TLTJjSAU3dExWtOuPK2KFK7gthwFz1ftVc8wnrgPGhfDTy8yYnsKJ1D5uF4FzxDuKBeIVibuy8MMVNXy4attuKJ9sl7jNH-10Zh8DJMb8L9KwhFpap5lpJb1DiWxtjlolEnUtMhoyMZjEaWxvloCPhZUJtftb1MHLdAwVkDbFwy43yDxKYL-fFiJWVdoW_ERfMOpyyKaJFKq_dTAGD2TyXTxQZ9pCJyY2tY6DaiZNu6bX-TVRgN2gjPzNDSUlo0KLVZ0GOZH9-zlRp1yxQyZ97TSOLGthQ2psSQ4zMJ3HCak0dJP_jCZw50Jyyw3PDm3r_pXbC1pA8ZzvriB7ZsA"; } public static String getExpiredAccessToken() { @@ -19,6 +20,7 @@ public static String getExpiredAccessToken() { public static String getInvalidToken() { return "Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTVFVNRUVUIiwic3ViIjoiMSIsImF1dGgiOiJGSVJTVF9MT0dJTiIsImV4cCI6MTcwOTA0MTM1Mn0.1dU2fb1wUgJeV8R1RAjFpKx3g3qToRZnft1lxSejL7o"; } + public static String getMockAccessToken() { return "Bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJTVFVNRUVUIiwic3ViIjoiMSIsImF1dGgiOiJGSVJTVF9MT0dJTiIsImV4cCI6MTcwOTA0MTM1Mn0.1dU2fb1wUgJeV8R1RAjFpKxBg3qToRZnft1lxSejL7o"; } diff --git a/src/test/java/com/stumeet/server/template/ApiTest.java b/src/test/java/com/stumeet/server/template/ApiTest.java index ecbaf37b..3a833e22 100644 --- a/src/test/java/com/stumeet/server/template/ApiTest.java +++ b/src/test/java/com/stumeet/server/template/ApiTest.java @@ -11,6 +11,7 @@ import org.springframework.restdocs.RestDocumentationContextProvider; import org.springframework.restdocs.RestDocumentationExtension; import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.jdbc.Sql; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; @@ -23,6 +24,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; @SpringBootTest +@Sql(scripts = "classpath:db/setup.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) +@Sql(scripts = "classpath:db/teardown.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @ExtendWith(RestDocumentationExtension.class) @ActiveProfiles("test") @Import({TestAwsS3Config.class}) diff --git a/src/test/resources/db/setup.sql b/src/test/resources/db/setup.sql new file mode 100644 index 00000000..501cee69 --- /dev/null +++ b/src/test/resources/db/setup.sql @@ -0,0 +1,3 @@ +INSERT INTO member (id, name, image, region, profession_id, role, auth_type, tier, experience, is_deleted, deleted_at) +VALUES (1, 'test', 'http://localhost:4572/user/1/profile/2024030416531039839905-b7e8-4ad3-9552-7d9cbc01cb14-test.jpg', + '서울', 1, 'FIRST_LOGIN', 'OAUTH', 'SEED', 0.0, false, null); \ No newline at end of file diff --git a/src/test/resources/db/teardown.sql b/src/test/resources/db/teardown.sql new file mode 100644 index 00000000..816158a4 --- /dev/null +++ b/src/test/resources/db/teardown.sql @@ -0,0 +1,6 @@ +SET FOREIGN_KEY_CHECKS = 0; + +TRUNCATE TABLE member; +TRUNCATE TABLE oauth_login; + +SET FOREIGN_KEY_CHECKS = 1; \ No newline at end of file