diff --git a/back/src/main/java/com/back/domain/member/member/controller/MemberController.java b/back/src/main/java/com/back/domain/member/member/controller/MemberController.java index 77f03baa..a11e5298 100644 --- a/back/src/main/java/com/back/domain/member/member/controller/MemberController.java +++ b/back/src/main/java/com/back/domain/member/member/controller/MemberController.java @@ -1,9 +1,6 @@ package com.back.domain.member.member.controller; -import com.back.domain.member.member.dto.LoginRequest; -import com.back.domain.member.member.dto.MenteeSignupRequest; -import com.back.domain.member.member.dto.MentorVerificationRequest; -import com.back.domain.member.member.dto.MentorSignupVerifyRequest; +import com.back.domain.member.member.dto.*; import com.back.domain.member.member.entity.Member; import com.back.domain.member.member.service.MemberService; import com.back.global.rq.Rq; @@ -113,4 +110,36 @@ public RsData deleteMember() { return new RsData<>("200-7", "회원 탈퇴가 완료되었습니다."); } + + @GetMapping("/me/mentee") + @Operation(summary = "멘티 마이페이지 조회") + public RsData getMenteeMyPage() { + Member currentUser = rq.getActor(); + MenteeMyPageResponse response = memberService.getMenteeMyPage(currentUser); + return new RsData<>("200-9", "멘티 정보 조회 성공", response); + } + + @PutMapping("/me/mentee") + @Operation(summary = "멘티 정보 수정") + public RsData updateMentee(@RequestBody MenteeUpdateRequest request) { + Member currentUser = rq.getActor(); + memberService.updateMentee(currentUser, request); + return new RsData<>("200-10", "멘티 정보 수정 성공"); + } + + @GetMapping("/me/mentor") + @Operation(summary = "멘토 마이페이지 조회") + public RsData getMentorMyPage() { + Member currentUser = rq.getActor(); + MentorMyPageResponse response = memberService.getMentorMyPage(currentUser); + return new RsData<>("200-11", "멘토 정보 조회 성공", response); + } + + @PutMapping("/me/mentor") + @Operation(summary = "멘토 정보 수정") + public RsData updateMentor(@RequestBody MentorUpdateRequest request) { + Member currentUser = rq.getActor(); + memberService.updateMentor(currentUser, request); + return new RsData<>("200-12", "멘토 정보 수정 성공"); + } } diff --git a/back/src/main/java/com/back/domain/member/member/dto/MenteeMyPageResponse.java b/back/src/main/java/com/back/domain/member/member/dto/MenteeMyPageResponse.java new file mode 100644 index 00000000..68410f91 --- /dev/null +++ b/back/src/main/java/com/back/domain/member/member/dto/MenteeMyPageResponse.java @@ -0,0 +1,22 @@ +package com.back.domain.member.member.dto; + +import com.back.domain.member.member.entity.Member; +import com.back.domain.member.mentee.entity.Mentee; + +public record MenteeMyPageResponse( + Long id, + String email, + String name, + String nickname, + Long jobId +) { + public static MenteeMyPageResponse from(Member member, Mentee mentee) { + return new MenteeMyPageResponse( + member.getId(), + member.getEmail(), + member.getName(), + member.getNickname(), + mentee.getJobId() + ); + } +} \ No newline at end of file diff --git a/back/src/main/java/com/back/domain/member/member/dto/MenteeUpdateRequest.java b/back/src/main/java/com/back/domain/member/member/dto/MenteeUpdateRequest.java new file mode 100644 index 00000000..15cd28b7 --- /dev/null +++ b/back/src/main/java/com/back/domain/member/member/dto/MenteeUpdateRequest.java @@ -0,0 +1,7 @@ +package com.back.domain.member.member.dto; + +public record MenteeUpdateRequest( + String nickname, + String interestedField +) { +} \ No newline at end of file diff --git a/back/src/main/java/com/back/domain/member/member/dto/MentorMyPageResponse.java b/back/src/main/java/com/back/domain/member/member/dto/MentorMyPageResponse.java new file mode 100644 index 00000000..18e24e37 --- /dev/null +++ b/back/src/main/java/com/back/domain/member/member/dto/MentorMyPageResponse.java @@ -0,0 +1,26 @@ +package com.back.domain.member.member.dto; + +import com.back.domain.member.member.entity.Member; +import com.back.domain.member.mentor.entity.Mentor; + +public record MentorMyPageResponse( + Long id, + String email, + String name, + String nickname, + Long jobId, + Double rate, + Integer careerYears +) { + public static MentorMyPageResponse from(Member member, Mentor mentor) { + return new MentorMyPageResponse( + member.getId(), + member.getEmail(), + member.getName(), + member.getNickname(), + mentor.getJobId(), + mentor.getRate(), + mentor.getCareerYears() + ); + } +} \ No newline at end of file diff --git a/back/src/main/java/com/back/domain/member/member/dto/MentorUpdateRequest.java b/back/src/main/java/com/back/domain/member/member/dto/MentorUpdateRequest.java new file mode 100644 index 00000000..9ff005ee --- /dev/null +++ b/back/src/main/java/com/back/domain/member/member/dto/MentorUpdateRequest.java @@ -0,0 +1,8 @@ +package com.back.domain.member.member.dto; + +public record MentorUpdateRequest( + String nickname, + String career, + Integer careerYears +) { +} \ No newline at end of file diff --git a/back/src/main/java/com/back/domain/member/member/entity/Member.java b/back/src/main/java/com/back/domain/member/member/entity/Member.java index 739588ab..356925a5 100644 --- a/back/src/main/java/com/back/domain/member/member/entity/Member.java +++ b/back/src/main/java/com/back/domain/member/member/entity/Member.java @@ -32,6 +32,10 @@ public enum Role { MENTOR, MENTEE, ADMIN } + public void updateNickname(String nickname) { + this.nickname = nickname; + } + public Member(String email, String password, String name, String nickname, Role role) { this.email = email; this.password = password; diff --git a/back/src/main/java/com/back/domain/member/member/service/MemberService.java b/back/src/main/java/com/back/domain/member/member/service/MemberService.java index a8d2d620..1ac4f20d 100644 --- a/back/src/main/java/com/back/domain/member/member/service/MemberService.java +++ b/back/src/main/java/com/back/domain/member/member/service/MemberService.java @@ -1,5 +1,9 @@ package com.back.domain.member.member.service; +import com.back.domain.member.member.dto.MenteeMyPageResponse; +import com.back.domain.member.member.dto.MenteeUpdateRequest; +import com.back.domain.member.member.dto.MentorMyPageResponse; +import com.back.domain.member.member.dto.MentorUpdateRequest; import com.back.domain.member.member.entity.Member; import com.back.domain.member.member.repository.MemberRepository; import com.back.domain.member.mentee.entity.Mentee; @@ -157,4 +161,71 @@ public Member refreshAccessToken(String refreshToken) { return memberRepository.findByEmail(email) .orElseThrow(() -> new ServiceException("401-5", "존재하지 않는 사용자입니다.")); } + + public MenteeMyPageResponse getMenteeMyPage(Member currentUser) { + Mentee mentee = menteeRepository.findByMemberId(currentUser.getId()) + .orElseThrow(() -> new ServiceException("404-2", "멘티 정보를 찾을 수 없습니다.")); + + return MenteeMyPageResponse.from(currentUser, mentee); + } + + @Transactional + public void updateMentee(Member currentUser, MenteeUpdateRequest request) { + // 닉네임 중복 체크 (본인 제외) + if (!currentUser.getNickname().equals(request.nickname())) { + memberRepository.findByNickname(request.nickname()).ifPresent( + member -> { + if (!member.getId().equals(currentUser.getId())) { + throw new ServiceException("400-3", "이미 존재하는 닉네임입니다."); + } + } + ); + } + + // Member 정보 업데이트 (닉네임) + Member member = memberRepository.findById(currentUser.getId()) + .orElseThrow(() -> new ServiceException("404-1", "존재하지 않는 회원입니다.")); + + member.updateNickname(request.nickname()); + memberRepository.save(member); + + // TODO: interestedField를 jobId로 매핑하는 로직 필요 (현재는 기존 jobId 유지) + } + + public MentorMyPageResponse getMentorMyPage(Member currentUser) { + Mentor mentor = mentorRepository.findByMemberId(currentUser.getId()) + .orElseThrow(() -> new ServiceException("404-3", "멘토 정보를 찾을 수 없습니다.")); + + return MentorMyPageResponse.from(currentUser, mentor); + } + + @Transactional + public void updateMentor(Member currentUser, MentorUpdateRequest request) { + // 닉네임 중복 체크 (본인 제외) + if (!currentUser.getNickname().equals(request.nickname())) { + memberRepository.findByNickname(request.nickname()).ifPresent( + member -> { + if (!member.getId().equals(currentUser.getId())) { + throw new ServiceException("400-4", "이미 존재하는 닉네임입니다."); + } + } + ); + } + + Mentor mentor = mentorRepository.findByMemberId(currentUser.getId()) + .orElseThrow(() -> new ServiceException("404-3", "멘토 정보를 찾을 수 없습니다.")); + + // Member 정보 업데이트 (닉네임) + Member member = memberRepository.findById(currentUser.getId()) + .orElseThrow(() -> new ServiceException("404-1", "존재하지 않는 회원입니다.")); + + member.updateNickname(request.nickname()); + memberRepository.save(member); + + // Mentor 정보 업데이트 (경력연수) + mentor.updateCareerYears(request.careerYears()); + mentorRepository.save(mentor); + + // TODO: career를 jobId로 매핑하는 로직 필요 (현재는 기존 jobId 유지) + } } diff --git a/back/src/main/java/com/back/domain/member/mentor/entity/Mentor.java b/back/src/main/java/com/back/domain/member/mentor/entity/Mentor.java index 4eac842e..a86b8fe1 100644 --- a/back/src/main/java/com/back/domain/member/mentor/entity/Mentor.java +++ b/back/src/main/java/com/back/domain/member/mentor/entity/Mentor.java @@ -31,4 +31,8 @@ public Mentor(Member member, Long jobId, Double rate, Integer careerYears) { this.rate = rate; this.careerYears = careerYears; } + + public void updateCareerYears(Integer careerYears) { + this.careerYears = careerYears; + } } diff --git a/back/src/test/java/com/back/domain/member/member/controller/MemberControllerTest.java b/back/src/test/java/com/back/domain/member/member/controller/MemberControllerTest.java index 544dfdba..b852205a 100644 --- a/back/src/test/java/com/back/domain/member/member/controller/MemberControllerTest.java +++ b/back/src/test/java/com/back/domain/member/member/controller/MemberControllerTest.java @@ -496,4 +496,226 @@ void t13() throws Exception { .andExpect(status().isUnauthorized()); } + @Test + @DisplayName("멘티 마이페이지 조회 성공") + void t14() throws Exception { + String email = "mentee@example.com"; + memberService.joinMentee(email, "멘티유저", "멘티닉네임", "password123", "Backend"); + + ResultActions loginResult = mvc.perform( + post("/auth/login") + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "email": "%s", + "password": "password123" + } + """, email)) + ); + + Cookie accessToken = loginResult.andReturn().getResponse().getCookie("accessToken"); + + ResultActions result = mvc + .perform( + get("/auth/me/mentee") + .cookie(accessToken) + ) + .andDo(print()); + + result + .andExpect(status().isOk()) + .andExpect(jsonPath("$.resultCode").value("200-9")) + .andExpect(jsonPath("$.msg").value("멘티 정보 조회 성공")) + .andExpect(jsonPath("$.data.email").value(email)) + .andExpect(jsonPath("$.data.name").value("멘티유저")) + .andExpect(jsonPath("$.data.nickname").value("멘티닉네임")); + } + + @Test + @DisplayName("멘티 정보 수정 성공") + void t15() throws Exception { + // 멘티 회원가입 + String email = "mentee2@example.com"; + memberService.joinMentee(email, "멘티유저2", "멘티닉네임2", "password123", "Frontend"); + + // 로그인하여 쿠키 받기 + ResultActions loginResult = mvc.perform( + post("/auth/login") + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "email": "%s", + "password": "password123" + } + """, email)) + ); + + Cookie accessToken = loginResult.andReturn().getResponse().getCookie("accessToken"); + + // 멘티 정보 수정 + ResultActions result = mvc + .perform( + put("/auth/me/mentee") + .cookie(accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "nickname": "새로운닉네임", + "interestedField": "Mobile" + } + """) + ) + .andDo(print()); + + result + .andExpect(status().isOk()) + .andExpect(jsonPath("$.resultCode").value("200-10")) + .andExpect(jsonPath("$.msg").value("멘티 정보 수정 성공")); + } + + @Test + @DisplayName("멘토 마이페이지 조회 성공") + void t16() throws Exception { + // 멘토 회원가입 + String email = "mentor@example.com"; + memberService.joinMentor(email, "멘토유저", "멘토닉네임", "password123", "Backend", 5); + + // 로그인하여 쿠키 받기 + ResultActions loginResult = mvc.perform( + post("/auth/login") + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "email": "%s", + "password": "password123" + } + """, email)) + ); + + Cookie accessToken = loginResult.andReturn().getResponse().getCookie("accessToken"); + + // 멘토 마이페이지 조회 + ResultActions result = mvc + .perform( + get("/auth/me/mentor") + .cookie(accessToken) + ) + .andDo(print()); + + result + .andExpect(status().isOk()) + .andExpect(jsonPath("$.resultCode").value("200-11")) + .andExpect(jsonPath("$.msg").value("멘토 정보 조회 성공")) + .andExpect(jsonPath("$.data.email").value(email)) + .andExpect(jsonPath("$.data.name").value("멘토유저")) + .andExpect(jsonPath("$.data.nickname").value("멘토닉네임")) + .andExpect(jsonPath("$.data.careerYears").value(5)); + } + + @Test + @DisplayName("멘토 정보 수정 성공") + void t17() throws Exception { + // 멘토 회원가입 + String email = "mentor2@example.com"; + memberService.joinMentor(email, "멘토유저2", "멘토닉네임2", "password123", "Frontend", 3); + + // 로그인하여 쿠키 받기 + ResultActions loginResult = mvc.perform( + post("/auth/login") + .contentType(MediaType.APPLICATION_JSON) + .content(String.format(""" + { + "email": "%s", + "password": "password123" + } + """, email)) + ); + + Cookie accessToken = loginResult.andReturn().getResponse().getCookie("accessToken"); + + // 멘토 정보 수정 + ResultActions result = mvc + .perform( + put("/auth/me/mentor") + .cookie(accessToken) + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "nickname": "새로운멘토닉네임", + "career": "Fullstack", + "careerYears": 7 + } + """) + ) + .andDo(print()); + + result + .andExpect(status().isOk()) + .andExpect(jsonPath("$.resultCode").value("200-12")) + .andExpect(jsonPath("$.msg").value("멘토 정보 수정 성공")); + } + + @Test + @DisplayName("로그인하지 않은 상태에서 멘티 마이페이지 접근 - 실패") + void t18() throws Exception { + ResultActions result = mvc + .perform(get("/auth/me/mentee")) + .andDo(print()); + + result + .andExpect(status().isUnauthorized()); + } + + @Test + @DisplayName("로그인하지 않은 상태에서 멘토 마이페이지 접근 - 실패") + void t19() throws Exception { + ResultActions result = mvc + .perform(get("/auth/me/mentor")) + .andDo(print()); + + result + .andExpect(status().isUnauthorized()); + } + + @Test + @DisplayName("로그인하지 않은 상태에서 멘티 정보 수정 - 실패") + void t20() throws Exception { + ResultActions result = mvc + .perform( + put("/auth/me/mentee") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "nickname": "새로운닉네임", + "interestedField": "Mobile" + } + """) + ) + .andDo(print()); + + result + .andExpect(status().isUnauthorized()); + } + + @Test + @DisplayName("로그인하지 않은 상태에서 멘토 정보 수정 - 실패") + void t21() throws Exception { + ResultActions result = mvc + .perform( + put("/auth/me/mentor") + .contentType(MediaType.APPLICATION_JSON) + .content(""" + { + "nickname": "새로운멘토닉네임", + "career": "Fullstack", + "careerYears": 7 + } + """) + ) + .andDo(print()); + + result + .andExpect(status().isUnauthorized()); + } + }