Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
dc042bd
📝 docs: SwaggerConfig 수정
seoshinehyo Feb 12, 2025
2bbb6b1
🔨 fix: 크롤링 정확도 개선
seoshinehyo Feb 12, 2025
dd472de
:recycle: [#129] refactor: 정적 팩토리 메서드명 수정
jher235 Feb 15, 2025
4ff9e24
:sparkles: [#129] feature: PostResponse dto 필드에 isMine 추가
jher235 Feb 15, 2025
8fcbf6e
:sparkles: [#129] feature: PostResponse dto 필드에 isMine 추가되어 로직 추가
jher235 Feb 15, 2025
91c84de
:recycle: [#129] refactor: post 클래스 생성자를 빌더로 통일
jher235 Feb 15, 2025
c930ab9
:memo: [#129] comment: PostImage 엔티티에서 생성자를 사용하는 이유에 대해 주석 작성
jher235 Feb 15, 2025
a2c833a
Merge pull request #130 from TeamMody/feature/#129-update-get-post-is…
jher235 Feb 16, 2025
3631c84
✨ [#132]feature:회원가입 완료 코드 구조, 인증 처리 수정
yunseo02 Feb 17, 2025
7d47456
♻️ [#132] refactor: 추천 좋아요 필드명 수정
yunseo02 Feb 17, 2025
c301b64
✨ feat : OpenAI 응답 개선
seoshinehyo Feb 17, 2025
9ed4e2e
Merge branch 'develop' of https://github.com/TeamMody/mody-server int…
seoshinehyo Feb 17, 2025
d9d992d
:recycle: [#131] refactor: 쿼리 최적화 및 코드 분리
jher235 Feb 17, 2025
ee7676b
✨ [#132] feature: 로그아웃 인증 설정 추가
yunseo02 Feb 17, 2025
c7a45ea
Merge branch 'develop' of https://github.com/TeamMody/mody-server int…
yunseo02 Feb 17, 2025
af58331
🔥 [#132] remove: 코드, 파일 삭제
yunseo02 Feb 17, 2025
e69986b
:recycle: [#131] refactor: 비즈니스 로직 분리
jher235 Feb 17, 2025
ab9c2b7
:recycle: [#131] refactor: 2가지 PostResponses를 병합하는 of 메서드 추가
jher235 Feb 17, 2025
40aa415
:recycle: [#131] refactor: PostResponses 로 response dto 이름 변경
jher235 Feb 17, 2025
e5a10c1
:sparkles: [#131] feature: 객체 비교를 위해 equals 및 hashcode 오버라이드
jher235 Feb 17, 2025
86fc091
:sparkles: [#131] feature: idx_member_post 인덱스 추가
jher235 Feb 17, 2025
e56e690
:sparkles: [#131] feature: idx_bodytype_post, idx_member_post 인덱스 추가
jher235 Feb 17, 2025
3050e60
Merge pull request #134 from TeamMody/refactor/#131-post-query
jher235 Feb 18, 2025
48aa446
♻️ refactor: 불필요한 메서드 제거
seoshinehyo Feb 18, 2025
6daec9b
♻️ [#135] refactor: 컨트롤러 책임 분리
seoshinehyo Feb 18, 2025
8cb21c0
♻️ [#135] 필요 없는 코드 제거
seoshinehyo Feb 18, 2025
0ae8c94
♻️ [#135] 체형 분석 API 최적화
seoshinehyo Feb 18, 2025
1e9f9b8
♻️ [#135] 체형 분석 결과 조회 API 최적화
seoshinehyo Feb 18, 2025
7f3a354
♻️ [#135] refactor: ChatGptService 코드 개선
seoshinehyo Feb 18, 2025
0f5c0d4
♻️ [#135] 핀터레스트 크롤링 개선
seoshinehyo Feb 18, 2025
abd4f1b
♻️ [#135] refactor: 불필요한 코드 제거
seoshinehyo Feb 18, 2025
9f47f32
♻️ [#135] refactor: setter -> builder 변경
seoshinehyo Feb 18, 2025
10547d3
🎨 [#132] refactor: jwtAuthenticationFilter에서 멤버 리포지토리를 서비스로 변경
yunseo02 Feb 18, 2025
b1722e8
Merge pull request #133 from TeamMody/feature/#132-회원가입-완료-오류-처리
yunseo02 Feb 18, 2025
5a7a0d0
♻️ [#135] refactor: 컨트롤러 책임 분리
seoshinehyo Feb 18, 2025
35c24c4
♻️ [#135] refactor: S3Service 리팩토링
seoshinehyo Feb 18, 2025
8df44a7
📝 docs: README 수정
seoshinehyo Feb 18, 2025
3e02e87
🐛 [137] fix: securityConfig 수정
yunseo02 Feb 18, 2025
56801fc
Merge pull request #138 from TeamMody/hotfix/#137-securityconfig-수정
seoshinehyo Feb 18, 2025
d2710c2
Merge pull request #136 from TeamMody/refactor/#135
seoshinehyo Feb 18, 2025
5ea3750
:sparkles: feat : 회원가입 로직 수정
ShimFFF Feb 19, 2025
4bb8405
Merge branch 'develop' of https://github.com/TeamMody/mody-server int…
ShimFFF Feb 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@

</div>

## 아키텍처 다이어그램
![image](https://github.com/user-attachments/assets/25a84d99-a57e-42c9-afcf-1f9cb9adc9da)


## 협업 규칙

### Github 협업 규칙
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ public class QMember extends EntityPathBase<Member> {

public final StringPath providerId = createString("providerId");

public final ListPath<com.example.mody.domain.recommendation.entity.Recommendation, com.example.mody.domain.recommendation.entity.QRecommendation> recommendations = this.<com.example.mody.domain.recommendation.entity.Recommendation, com.example.mody.domain.recommendation.entity.QRecommendation>createList("recommendations", com.example.mody.domain.recommendation.entity.Recommendation.class, com.example.mody.domain.recommendation.entity.QRecommendation.class, PathInits.DIRECT2);
public final ListPath<com.example.mody.domain.recommendation.entity.mapping.MemberRecommendationLike, com.example.mody.domain.recommendation.entity.mapping.QMemberRecommendationLike> recommendationLikes = this.<com.example.mody.domain.recommendation.entity.mapping.MemberRecommendationLike, com.example.mody.domain.recommendation.entity.mapping.QMemberRecommendationLike>createList("recommendationLikes", com.example.mody.domain.recommendation.entity.mapping.MemberRecommendationLike.class, com.example.mody.domain.recommendation.entity.mapping.QMemberRecommendationLike.class, PathInits.DIRECT2);

public final ListPath<com.example.mody.domain.recommendation.entity.mapping.MemberRecommendationLike, com.example.mody.domain.recommendation.entity.mapping.QMemberRecommendationLike> RecommendLikes = this.<com.example.mody.domain.recommendation.entity.mapping.MemberRecommendationLike, com.example.mody.domain.recommendation.entity.mapping.QMemberRecommendationLike>createList("RecommendLikes", com.example.mody.domain.recommendation.entity.mapping.MemberRecommendationLike.class, com.example.mody.domain.recommendation.entity.mapping.QMemberRecommendationLike.class, PathInits.DIRECT2);
public final ListPath<com.example.mody.domain.recommendation.entity.Recommendation, com.example.mody.domain.recommendation.entity.QRecommendation> recommendations = this.<com.example.mody.domain.recommendation.entity.Recommendation, com.example.mody.domain.recommendation.entity.QRecommendation>createList("recommendations", com.example.mody.domain.recommendation.entity.Recommendation.class, com.example.mody.domain.recommendation.entity.QRecommendation.class, PathInits.DIRECT2);

public final NumberPath<Integer> reportCount = createNumber("reportCount", Integer.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public class QRecommendation extends EntityPathBase<Recommendation> {

public final com.example.mody.domain.member.entity.QMember member;

public final ListPath<com.example.mody.domain.recommendation.entity.mapping.MemberRecommendationLike, com.example.mody.domain.recommendation.entity.mapping.QMemberRecommendationLike> RecommendLikes = this.<com.example.mody.domain.recommendation.entity.mapping.MemberRecommendationLike, com.example.mody.domain.recommendation.entity.mapping.QMemberRecommendationLike>createList("RecommendLikes", com.example.mody.domain.recommendation.entity.mapping.MemberRecommendationLike.class, com.example.mody.domain.recommendation.entity.mapping.QMemberRecommendationLike.class, PathInits.DIRECT2);
public final ListPath<com.example.mody.domain.recommendation.entity.mapping.MemberRecommendationLike, com.example.mody.domain.recommendation.entity.mapping.QMemberRecommendationLike> recommendationLikes = this.<com.example.mody.domain.recommendation.entity.mapping.MemberRecommendationLike, com.example.mody.domain.recommendation.entity.mapping.QMemberRecommendationLike>createList("recommendationLikes", com.example.mody.domain.recommendation.entity.mapping.MemberRecommendationLike.class, com.example.mody.domain.recommendation.entity.mapping.QMemberRecommendationLike.class, PathInits.DIRECT2);

public final EnumPath<com.example.mody.domain.recommendation.enums.RecommendType> recommendType = createEnum("recommendType", com.example.mody.domain.recommendation.enums.RecommendType.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,26 +96,25 @@ public class AuthController {
)
),
@ApiResponse(
responseCode = "404",
description = "사용자를 찾을 수 없음",
responseCode = "COMMON401",
description = "로그인하지 않은 경우에 발생합니다.(엑세스 토큰을 넣지 않았을 때)",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"timestamp": "2024-01-13T10:00:00",
"code": "MEMBER404",
"message": "해당 회원을 찾을 수 없습니다.",
"result": null
}
"""
{
"timestamp": "2025-02-17T22:23:22.7640118",
"code": "COMMON401",
"message": "인증이 필요합니다."
}
"""
)
)
)
})
@PostMapping("/signup/complete")
public BaseResponse<Void> completeRegistration(
@AuthenticationPrincipal CustomUserDetails userDetails,
@AuthenticationPrincipal CustomUserDetails customUserDetails,
@Valid @RequestBody
@Parameter(
description = "회원가입 완료 요청 정보",
Expand All @@ -137,7 +136,7 @@ public BaseResponse<Void> completeRegistration(
)
) MemberRegistrationRequest request
) {
memberCommandService.completeRegistration(userDetails.getMember().getId(), request);
memberCommandService.completeRegistration(customUserDetails.getMember(), request);
return BaseResponse.onSuccess(null);
}

Expand Down Expand Up @@ -243,18 +242,17 @@ public BaseResponse<AccessTokenResponse> reissueToken(
)
),
@ApiResponse(
responseCode = "AUTH401",
responseCode = "COMMON401",
description = "인증되지 않은 사용자",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"timestamp": "2024-01-13T10:00:00",
"code": "AUTH001",
"message": "JWT가 없습니다.",
"result": null
}
"timestamp": "2025-02-17T22:23:22.7640118",
"code": "COMMON401",
"message": "인증이 필요합니다."
}
"""
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
Member member = memberRepository.findByProviderId(oAuth2User.getOAuth2Response().getProviderId())
.orElseGet(() -> saveMember(oAuth2User));

// 새로 가입한 멤버인지 아닌지 확인
boolean isNewMember = member.getCreatedAt().equals(member.getUpdatedAt());
// // 새로 가입한 멤버인지 아닌지 확인
// boolean isNewMember = member.getCreatedAt().equals(member.getUpdatedAt());

// Access Token, Refresh Token 발급
String newAccessToken = authCommandService.processLoginSuccess(member, response);

String tempUrl = (isNewMember || !member.isRegistrationCompleted()) ? FRONT_SIGNUP_URL : FRONT_HOME_URL;
String tempUrl = (!member.isRegistrationCompleted()) ? FRONT_SIGNUP_URL : FRONT_HOME_URL;

String targetUrl = UriComponentsBuilder.fromUriString(tempUrl)
.build().toUriString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@
public class JwtAuthenticationFilter extends OncePerRequestFilter {

private final JwtProvider jwtProvider;
private final MemberRepository memberRepository;
private final ObjectMapper objectMapper;
private final MemberQueryService memberQueryService;
private final ObjectMapper objectMapper;

@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
Expand All @@ -47,7 +46,9 @@ protected boolean shouldNotFilter(HttpServletRequest request) throws ServletExce
uri = uri.substring(contextPath.length());
}
log.info("JwtAuthenticationFilter - Request URI after context removal: {}", uri);
boolean skip = uri.startsWith("/auth/") && !uri.startsWith("/auth/signup/complete") ||
boolean skip = uri.startsWith("/auth/")
&& !uri.startsWith("/auth/signup/complete")
&& !uri.startsWith("/auth/logout")||
uri.startsWith("/oauth2/") ||
uri.startsWith("/email/") ||
uri.startsWith("/swagger-ui/") ||
Expand All @@ -70,8 +71,7 @@ protected void doFilterInternal(
// 만약 토큰이 있다면
if (token != null) {
String memberId = jwtProvider.validateTokenAndGetSubject(token);
Member member = memberRepository.findById(Long.parseLong(memberId))
.orElseThrow(() -> new RestApiException(AuthErrorStatus.INVALID_ACCESS_TOKEN));
Member member = memberQueryService.findMemberById(Long.parseLong(memberId));

CustomUserDetails customUserDetails = new CustomUserDetails(member);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ private Member saveMember(OAuth2Response oAuth2Response) {
Member member = Member.builder()
.providerId(oAuth2Response.getProviderId())
.provider(oAuth2Response.getProvider())
.nickname(oAuth2Response.getName())
.status(Status.ACTIVE)
.role(Role.ROLE_USER)
.loginType(LoginType.KAKAO)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,149 +7,32 @@
import com.example.mody.domain.bodytype.service.memberbodytype.MemberBodyTypeQueryService;
import com.example.mody.global.common.base.BaseResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

@Tag(name = "체형 분석", description = "체형 분석 API")
@Tag(name = "체형 분석", description = "체형 분석 관련 API")
@RestController
@RequestMapping("/body-analysis")
@RequiredArgsConstructor
public class BodyTypeController {
public class BodyTypeController implements BodyTypeControllerInterface {

private final MemberBodyTypeCommandService memberBodyTypeCommandService;
private final MemberBodyTypeQueryService memberBodyTypeQueryService;

@PostMapping("/result")
@Operation(summary = "체형 분석 API", description = "OpenAi를 사용해서 사용자의 체형을 분석하는 API입니다. Request Body에는 질문에 맞는 답변 목록을 보내주세요.")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200",description = "체형 분석 성공"),
@ApiResponse(
responseCode = "401",
description = "Access Token이 필요합니다.",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"timestamp": "2025-01-26T15:15:54.334Z",
"code": "COMMON401",
"message": "인증이 필요합니다."
}
"""
)
)
),
@ApiResponse(
responseCode = "404",
description = "체형을 찾을 수 없습니다.",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"timestamp": "2025-01-26T15:15:54.334Z",
"code": "BODY_TYPE404",
"message": "체형을 찾을 수 없습니다."
}
"""
)
)
),
@ApiResponse(
responseCode = "500",
description = "GPT가 적절한 응답을 하지 못 했습니다.",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"timestamp": "2025-01-26T15:15:54.334Z",
"code": "ANALYSIS108",
"message": "GPT가 올바르지 않은 답변을 했습니다. 관리자에게 문의하세요."
}
"""
)
)
),
})
@Operation(summary = "체형 분석 API", description = "OpenAI를 사용해서 사용자의 체형을 분석하는 API입니다. Request Body에는 질문에 맞는 답변 목록을 보내주세요.")
public BaseResponse<BodyTypeAnalysisResponse> analyzeBodyType(
@AuthenticationPrincipal CustomUserDetails customUserDetails,
@Valid @RequestBody BodyTypeAnalysisRequest request
) {
return BaseResponse.onSuccess(memberBodyTypeCommandService.analyzeBodyType(customUserDetails.getMember(), request.getAnswer()));
}

// @GetMapping()
// @Operation(summary = "체형 질문 문항 조회 API - 프론트와 협의 필요(API 연동 안 해도 됨)",
// description = "체형 분석을 하기 위해 질문 문항을 받아 오는 API입니다. 이 부분은 서버에서 바로 프롬프트로 넣는 방법도 있기 때문에 프론트와 협의 후 진행하겠습니다.")
// @ApiResponses({
// @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200",description = "OK, 성공")
// })
// public BaseResponse<Void> getQuestion() {
// return BaseResponse.onSuccess(null);
// }

@GetMapping("/result")
@Operation(summary = "체형 분석 결과 조회 API", description = "사용자의 체형 분석 결과를 받아 오는 API입니다.")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200",description = "체형 분석 결과 조회 성공"),
@ApiResponse(
responseCode = "400",
description = "체형 분석 결과를 처리하는 중 JSON 파싱에 실패했습니다.",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"timestamp": "2025-01-26T15:15:54.334Z",
"code": "JSON_PARSING400",
"message": "체형 분석 결과를 처리하는 중 JSON 파싱에 실패했습니다."
}
"""
)
)
),
@ApiResponse(
responseCode = "401",
description = "Access Token이 필요합니다.",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"timestamp": "2025-01-26T15:15:54.334Z",
"code": "COMMON401",
"message": "인증이 필요합니다."
}
"""
)
)
),
@ApiResponse(
responseCode = "404",
description = "체형 분석 결과를 찾을 수 없습니다.",
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"timestamp": "2025-01-26T15:15:54.334Z",
"code": "MEMBER_BODY_TYPE404",
"message": "체형 분석 결과를 찾을 수 없습니다."
}
"""
)
)
),
})
public BaseResponse<BodyTypeAnalysisResponse> getBodyType(@AuthenticationPrincipal CustomUserDetails customUserDetails) {
return BaseResponse.onSuccess(memberBodyTypeQueryService.getBodyTypeAnalysis(customUserDetails.getMember()));
}
Expand Down
Loading