Repository navigation
[Feat/#84] 로그인용 회원 가입·갱신·약관 동의 조회·학적 상태 추가 #89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
143de60
chore: core:domain:auth Gradle 모듈 좌표·jar 이름을 gateway:auth와 분리
tnals0924 254ff19
feat: auth 도메인 OAuth 전략 인터페이스와 서비스 추가
tnals0924 afcf1d8
feat: OAuth 계정 연결 테이블과 JPA 저장소 추가
tnals0924 f495503
docs: 요청별 구현체 선택 레지스트리 컨벤션 추가
tnals0924 cc5637b
feat: 로그인용 회원 가입·갱신과 학적 상태 추가
tnals0924 aaf22a3
feat: 필수 약관 동의 여부 조회 추가
tnals0924 a9298f7
refactor: OAuth 계정 단건 조회의 불필요한 readOnly 트랜잭션 제거
tnals0924 2ec5485
Merge branch 'feat/#83-oauth-auth-domain' into feat/#84-member-oauth-…
tnals0924 702c0f4
refactor: Department.fromMajor의 null 검사를 메서드 진입부로 이동
tnals0924 1899e98
refactor: 필수 약관 여부를 TermType의 required 필드로 이동
tnals0924 85d042f
refactor: 로그인 회원 가입·갱신 분기를 Optional 체인에서 조건문으로 변경
tnals0924 9902070
refactor: 프로필 값이 바뀐 경우에만 회원을 저장
tnals0924 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
.../domain/auth/src/main/java/kr/ac/kookmin/stream/auth/domain/oauth/client/OAuthClient.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package kr.ac.kookmin.stream.auth.domain.oauth.client; | ||
|
|
||
| import kr.ac.kookmin.stream.auth.domain.oauth.domain.OAuthLoginCommand; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.domain.OAuthProvider; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.domain.OAuthUserInfo; | ||
|
|
||
| /** | ||
| * provider별 OAuth 로그인 전략. 구현체는 provider마다 하나씩 모두 빈으로 등록하고, | ||
| * 요청의 provider에 맞는 구현체를 OAuthClientRegistry가 고른다. | ||
| */ | ||
| public interface OAuthClient { | ||
|
|
||
| OAuthProvider provider(); | ||
|
|
||
| /** provider에 등록한 redirect URI와 정확히 일치하는지. 외부 호출 전에 거르는 용도다. */ | ||
| boolean isAllowedRedirectUri(String redirectUri); | ||
|
|
||
| /** | ||
| * code를 provider 토큰으로 교환하고 사용자 정보를 조회한다. | ||
| * provider 토큰은 이 메서드 안에서만 쓰고 밖으로 내보내지 않는다. | ||
| */ | ||
| OAuthUserInfo fetchUserInfo(OAuthLoginCommand command); | ||
| } |
28 changes: 28 additions & 0 deletions
28
...domain/auth/src/main/java/kr/ac/kookmin/stream/auth/domain/oauth/domain/OAuthAccount.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package kr.ac.kookmin.stream.auth.domain.oauth.domain; | ||
|
|
||
| import lombok.AccessLevel; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.EqualsAndHashCode; | ||
| import lombok.Getter; | ||
|
|
||
| /** | ||
| * provider 계정과 회원의 연결. 한 회원은 provider마다 계정을 하나씩 연결할 수 있다. | ||
| */ | ||
| @Getter | ||
| @EqualsAndHashCode | ||
| @AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
| public class OAuthAccount { | ||
|
|
||
| private Long id; | ||
| private Long memberId; | ||
| private OAuthProvider provider; | ||
| private String providerUserId; | ||
|
|
||
| public static OAuthAccount create(Long memberId, OAuthProvider provider, String providerUserId) { | ||
| return new OAuthAccount(null, memberId, provider, providerUserId); | ||
| } | ||
|
|
||
| public static OAuthAccount of(Long id, Long memberId, OAuthProvider provider, String providerUserId) { | ||
| return new OAuthAccount(id, memberId, provider, providerUserId); | ||
| } | ||
| } |
21 changes: 21 additions & 0 deletions
21
...main/auth/src/main/java/kr/ac/kookmin/stream/auth/domain/oauth/domain/OAuthErrorCode.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package kr.ac.kookmin.stream.auth.domain.oauth.domain; | ||
|
|
||
| import kr.ac.kookmin.stream.common.ErrorCode; | ||
| import kr.ac.kookmin.stream.common.ErrorStatus; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
| import lombok.experimental.Accessors; | ||
|
|
||
| @Getter | ||
| @Accessors(fluent = true) | ||
| @AllArgsConstructor | ||
| public enum OAuthErrorCode implements ErrorCode { | ||
|
|
||
| UNSUPPORTED_OAUTH_PROVIDER(ErrorStatus.BAD_REQUEST, "지원하지 않는 로그인 방식입니다."), | ||
| REDIRECT_URI_NOT_ALLOWED(ErrorStatus.BAD_REQUEST, "허용되지 않은 redirect URI입니다."), | ||
| INVALID_AUTHORIZATION_CODE(ErrorStatus.UNAUTHORIZED, "로그인이 만료되었습니다. 다시 로그인해 주세요."), | ||
| OAUTH_PROVIDER_UNAVAILABLE(ErrorStatus.BAD_GATEWAY, "로그인 서버에 연결할 수 없습니다. 잠시 후 다시 시도해 주세요."); | ||
|
|
||
| private final int status; | ||
| private final String message; | ||
| } |
8 changes: 8 additions & 0 deletions
8
...n/auth/src/main/java/kr/ac/kookmin/stream/auth/domain/oauth/domain/OAuthLoginCommand.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package kr.ac.kookmin.stream.auth.domain.oauth.domain; | ||
|
|
||
| public record OAuthLoginCommand( | ||
| OAuthProvider provider, | ||
| String code, | ||
| String codeVerifier, | ||
| String redirectUri | ||
| ) {} |
16 changes: 16 additions & 0 deletions
16
...omain/auth/src/main/java/kr/ac/kookmin/stream/auth/domain/oauth/domain/OAuthProvider.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package kr.ac.kookmin.stream.auth.domain.oauth.domain; | ||
|
|
||
| import java.util.Arrays; | ||
| import kr.ac.kookmin.stream.common.BusinessException; | ||
|
|
||
| public enum OAuthProvider { | ||
| KCONNECT; | ||
|
|
||
| /** 로그인 경로의 provider 값(예: "kconnect")을 대소문자 구분 없이 변환한다. */ | ||
| public static OAuthProvider from(String value) { | ||
| return Arrays.stream(values()) | ||
| .filter(provider -> provider.name().equalsIgnoreCase(value)) | ||
| .findFirst() | ||
| .orElseThrow(() -> new BusinessException(OAuthErrorCode.UNSUPPORTED_OAUTH_PROVIDER)); | ||
| } | ||
| } |
17 changes: 17 additions & 0 deletions
17
...omain/auth/src/main/java/kr/ac/kookmin/stream/auth/domain/oauth/domain/OAuthUserInfo.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package kr.ac.kookmin.stream.auth.domain.oauth.domain; | ||
|
|
||
| /** | ||
| * provider에서 확인한 사용자 정보. | ||
| * <p> | ||
| * providerUserId는 provider 안에서 바뀌지 않는 고유 ID로, 회원을 찾는 키가 된다. | ||
| * studentId·major·academicStatus는 학번·학과·학적을 주지 않는 provider(구글·카카오)에서 null일 수 있다. | ||
| * academicStatus는 provider가 주는 학적 상태 값을 그대로 담는다. | ||
| */ | ||
| public record OAuthUserInfo( | ||
| OAuthProvider provider, | ||
| String providerUserId, | ||
| String studentId, | ||
| String name, | ||
| String major, | ||
| String academicStatus | ||
| ) {} |
10 changes: 10 additions & 0 deletions
10
...c/main/java/kr/ac/kookmin/stream/auth/domain/oauth/repository/OAuthAccountRepository.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package kr.ac.kookmin.stream.auth.domain.oauth.repository; | ||
|
|
||
| import java.util.Optional; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.domain.OAuthAccount; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.domain.OAuthProvider; | ||
|
|
||
| public interface OAuthAccountRepository { | ||
| Optional<OAuthAccount> findByProviderAndProviderUserId(OAuthProvider provider, String providerUserId); | ||
| OAuthAccount save(OAuthAccount oauthAccount); | ||
| } |
12 changes: 12 additions & 0 deletions
12
...omain/auth/src/main/java/kr/ac/kookmin/stream/auth/domain/oauth/service/OAuthService.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| package kr.ac.kookmin.stream.auth.domain.oauth.service; | ||
|
|
||
| import java.util.Optional; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.domain.OAuthLoginCommand; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.domain.OAuthProvider; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.domain.OAuthUserInfo; | ||
|
|
||
| public interface OAuthService { | ||
| OAuthUserInfo authenticate(OAuthLoginCommand command); | ||
| Optional<Long> findMemberId(OAuthProvider provider, String providerUserId); | ||
| void link(OAuthProvider provider, String providerUserId, Long memberId); | ||
| } |
42 changes: 42 additions & 0 deletions
42
...rc/main/java/kr/ac/kookmin/stream/auth/domain/oauth/service/impl/OAuthClientRegistry.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package kr.ac.kookmin.stream.auth.domain.oauth.service.impl; | ||
|
|
||
| import java.util.EnumMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
| import java.util.function.Function; | ||
| import java.util.stream.Collectors; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.client.OAuthClient; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.domain.OAuthErrorCode; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.domain.OAuthProvider; | ||
| import kr.ac.kookmin.stream.common.BusinessException; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| /** | ||
| * 빈으로 등록된 OAuthClient 구현체를 provider별로 모아 두고, 요청의 provider에 맞는 구현체를 돌려준다. | ||
| * 같은 provider의 구현체가 둘이면 기동 시 실패한다. | ||
| */ | ||
| @Component | ||
| class OAuthClientRegistry { | ||
|
|
||
| private final Map<OAuthProvider, OAuthClient> clients; | ||
|
|
||
| OAuthClientRegistry(List<OAuthClient> clients) { | ||
| this.clients = clients.stream() | ||
| .collect(Collectors.toMap( | ||
| OAuthClient::provider, | ||
| Function.identity(), | ||
| (first, second) -> { | ||
| throw new IllegalStateException("OAuthClient 구현체가 중복됐습니다: " + first.provider()); | ||
| }, | ||
| () -> new EnumMap<>(OAuthProvider.class) | ||
| )); | ||
| } | ||
|
|
||
| OAuthClient get(OAuthProvider provider) { | ||
| OAuthClient client = clients.get(provider); | ||
| if (client == null) { | ||
| throw new BusinessException(OAuthErrorCode.UNSUPPORTED_OAUTH_PROVIDER); | ||
| } | ||
| return client; | ||
| } | ||
| } |
46 changes: 46 additions & 0 deletions
46
...h/src/main/java/kr/ac/kookmin/stream/auth/domain/oauth/service/impl/OAuthServiceImpl.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| package kr.ac.kookmin.stream.auth.domain.oauth.service.impl; | ||
|
|
||
| import java.util.Optional; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.client.OAuthClient; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.domain.OAuthAccount; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.domain.OAuthErrorCode; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.domain.OAuthLoginCommand; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.domain.OAuthProvider; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.domain.OAuthUserInfo; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.repository.OAuthAccountRepository; | ||
| import kr.ac.kookmin.stream.auth.domain.oauth.service.OAuthService; | ||
| import kr.ac.kookmin.stream.common.BusinessException; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| @Service | ||
| @RequiredArgsConstructor | ||
| class OAuthServiceImpl implements OAuthService { | ||
|
|
||
| private final OAuthClientRegistry oauthClientRegistry; | ||
| private final OAuthAccountRepository oauthAccountRepository; | ||
|
|
||
| // 외부 호출만 하므로 트랜잭션을 걸지 않는다 | ||
| @Override | ||
| public OAuthUserInfo authenticate(OAuthLoginCommand command) { | ||
| OAuthClient client = oauthClientRegistry.get(command.provider()); | ||
| if (!client.isAllowedRedirectUri(command.redirectUri())) { | ||
| throw new BusinessException(OAuthErrorCode.REDIRECT_URI_NOT_ALLOWED); | ||
| } | ||
|
|
||
| return client.fetchUserInfo(command); | ||
| } | ||
|
|
||
| @Override | ||
| public Optional<Long> findMemberId(OAuthProvider provider, String providerUserId) { | ||
| return oauthAccountRepository.findByProviderAndProviderUserId(provider, providerUserId) | ||
| .map(OAuthAccount::getMemberId); | ||
| } | ||
|
|
||
| @Override | ||
| @Transactional | ||
| public void link(OAuthProvider provider, String providerUserId, Long memberId) { | ||
| oauthAccountRepository.save(OAuthAccount.create(memberId, provider, providerUserId)); | ||
| } | ||
| } |
8 changes: 8 additions & 0 deletions
8
core/domain/auth/src/main/java/kr/ac/kookmin/stream/auth/package-info.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| /** | ||
| * 계층 패키지(domain/{도메인}/{domain|repository|service})를 그대로 공개하기 위해 OPEN으로 둔다. | ||
| * service.impl 접근 차단은 bootstrap의 DomainImplAccessTests(ArchUnit)가 담당한다. | ||
| */ | ||
| @ApplicationModule(type = ApplicationModule.Type.OPEN) | ||
| package kr.ac.kookmin.stream.auth; | ||
|
|
||
| import org.springframework.modulith.ApplicationModule; |
23 changes: 21 additions & 2 deletions
23
...ain/member/src/main/java/kr/ac/kookmin/stream/member/domain/member/domain/Department.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,28 @@ | ||
| package kr.ac.kookmin.stream.member.domain.member.domain; | ||
|
|
||
| import java.util.Arrays; | ||
| import kr.ac.kookmin.stream.common.BusinessException; | ||
| import lombok.AllArgsConstructor; | ||
|
|
||
| /** | ||
| * 학부. 학생회 부서(CouncilDepartment)와는 다른 개념이다. | ||
| */ | ||
| @AllArgsConstructor | ||
| public enum Department { | ||
| AI, | ||
| SW | ||
| AI("인공지능전공"), | ||
| SW("소프트웨어전공"); | ||
|
|
||
| // 로그인 provider가 주는 소속 문자열에서 찾는 전공명 | ||
| private final String majorName; | ||
|
|
||
| /** 소속 문자열을 학부로 바꾼다. 소프트웨어융합대학 전공이 아니면 가입할 수 없다. */ | ||
| public static Department fromMajor(String major) { | ||
| if (major == null) { | ||
| throw new BusinessException(MemberErrorCode.DEPARTMENT_NOT_ALLOWED); | ||
| } | ||
| return Arrays.stream(values()) | ||
| .filter(department -> major.contains(department.majorName)) | ||
| .findFirst() | ||
| .orElseThrow(() -> new BusinessException(MemberErrorCode.DEPARTMENT_NOT_ALLOWED)); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
.../src/main/java/kr/ac/kookmin/stream/member/domain/member/domain/MemberProfileCommand.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package kr.ac.kookmin.stream.member.domain.member.domain; | ||
|
|
||
| /** | ||
| * 로그인 provider가 준 회원 프로필. major·academicStatus는 원문이며, 학부 변환은 {@link Department#fromMajor}가 한다. | ||
| */ | ||
| public record MemberProfileCommand( | ||
| String studentId, | ||
| String name, | ||
| String major, | ||
| String academicStatus | ||
| ) {} |
16 changes: 14 additions & 2 deletions
16
...omain/member/src/main/java/kr/ac/kookmin/stream/member/domain/member/domain/TermType.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,18 @@ | ||
| package kr.ac.kookmin.stream.member.domain.member.domain; | ||
|
|
||
| import lombok.AccessLevel; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
| import lombok.experimental.Accessors; | ||
|
|
||
| @Getter | ||
| @Accessors(fluent = true) | ||
| @AllArgsConstructor(access = AccessLevel.PRIVATE) | ||
| public enum TermType { | ||
| PRIVACY_POLICY, // 개인정보 이용 동의 | ||
| TERMS_OF_SERVICE // 서비스 이용 약관 동의 | ||
|
|
||
| PRIVACY_POLICY(true), // 개인정보 이용 동의 | ||
| TERMS_OF_SERVICE(true); // 서비스 이용 약관 동의 | ||
|
|
||
| /** 가입 시 반드시 동의해야 하는 약관인지 여부 */ | ||
| private final boolean required; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift
프로필 변경을 불변 도메인 객체 반환으로 구현해 주세요.
추가한 메서드는
Member의 필드를 직접 변경합니다.Member를 불변record로 전환하고,updateProfile이 변경된Member를 반환하도록 구현해 주세요. 서비스의 두 갱신 경로도 반환된 객체를 저장해야 합니다.경로 지침에 포함된
docs/conventions/coding-style.md의 “도메인 객체는 JPA 어노테이션 없이 불변 record로 선언” 규칙에 근거합니다.🤖 Prompt for AI Agents
Source: Path instructions