Skip to content

feat(team): 팀·팀원 엔티티와 멤버십 조회 기반 추가 - #64

Merged
soyuo merged 6 commits into
developfrom
feat/58-team-entities-membership
Nov 6, 2026
Merged

soyuo merged 6 commits into
developfrom
feat/58-team-entities-membership

Conversation

@soyuo

@soyuo soyuo commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • 팀 저장 구조: Team·TeamMember 엔티티와 V3 마이그레이션을 추가하고, 중복 멤버십과 복수 팀장을 DB에서 막음
  • 권한 조회: TeamService가 TeamReader·TeamMembershipReader를 구현하고, 멤버십을 요청마다 DB에서 조회하도록 연결
  • 팀원 id·로컬 시드: 계약의 mbr- id 생성기와 local 프로필 전용 테스트 사용자·팀 시드 추가

Test plan

  • ./gradlew compileKotlin compileTestKotlin ktlintCheck
  • ./gradlew test --tests 'com.gsmsv.api.team.service.TeamServiceTest' --tests 'com.gsmsv.api.team.domain.TeamTest' --tests 'com.gsmsv.api.global.grn.IdGeneratorTest' --tests 'com.gsmsv.api.global.authorization.*'
  • ./gradlew test --tests 'com.gsmsv.api.team.TeamPersistenceTest' (Docker 데몬이 없어 PostgreSQL 통합 테스트 실행 불가)

관련 이슈

Closes #58

확인

  • 권한 판단을 자원 로더 밖에서 하지 않았다 (22번 권한 판단 Layer)
  • GRN 문자열을 직접 자르지 않고 GRN 파서만 썼다. GRN의 소유자 칸 값을 어떤 판단에도 쓰지 않았다 (23번 자원 이름 규칙) 해당 없음
  • 비밀값·내부 IP·포트·서버 주소 같은 운영 정보를 넣지 않았다 (공개 저장소)

@soyuo soyuo added the enhancement New feature or request label Nov 5, 2026
@sandrone-code-review

sandrone-code-review Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Pull Request 요약

AuthorizationBeanConfig에 Authorization 관련 Bean을 신규 등록하여 인증 설정을 강화했습니다. TeamPersistenceTest의 @SpringBootTest 어노테이션에서 properties 배열 문법 오류가 발견되어 수정이 필요합니다. 팀·팀원 엔티티와 V3 마이그레이션, 팀원 ID 생성기, 로컬 시드 데이터, 그리고 TeamService에 권한 조회 메서드를 추가하여 팀 관리 기능을 확장했습니다.

복수 모델 · 입력 25,467 · 출력 2,910 · 합계 28,377 토큰

Comment on lines +28 to +30
fun changeRole(role: TeamRole) {
this.role = role
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

중요 TeamMember.changeRole이 팀장 유일성을 보장하지 않는다

TeamMember.changeRole이 팀장 유일성 제약을 확인하지 않고 역할을 변경합니다. 팀에 이미 팀장이 있으면 두 번째 팀장이 생길 수 있습니다. 서비스 계층에서 중복 팀장을 방지하지 않으면 DB 부분 고유 인덱스가 위반되어 운영 장애로 이어집니다.

제안:

    fun changeRole(role: TeamRole) {
        this.role = role
    }

복수 모델


@Transactional(readOnly = true)
override fun findMemberships(userId: String): Map<String, TeamRole> =
teamMemberRepository.findAllByUserId(userId).associate { it.teamId to it.role }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

중요 findMemberships가 중복 팀 역할을 덮어쓴다

TeamService.findMemberships가 associate로 팀 id를 키로 삼아 중복 팀이 있으면 마지막 역할만 남깁니다. 사용자가 한 팀에 여러 멤버십을 가지는 것은 UNIQUE 제약으로 막히지 않으므로 데이터 무결성 문제가 될 수 있습니다. 팀당 하나의 역할만 반환하는 계약 의도가 아니라면 중복을 처리하거나 예외를 던져야 합니다.

제안:

        teamMemberRepository.findAllByUserId(userId).associate { it.teamId to it.role }

복수 모델

Comment on lines +52 to +63
fun approve() {
check(status == TeamStatus.PENDING) { "승인 대기 중인 팀만 승인할 수 있습니다." }
status = TeamStatus.APPROVED
rejectionReason = null
}

fun reject(reason: String) {
check(status == TeamStatus.PENDING) { "승인 대기 중인 팀만 거절할 수 있습니다." }
require(reason.isNotBlank()) { "거절 사유는 비어 있을 수 없습니다." }
status = TeamStatus.REJECTED
rejectionReason = reason
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

경미 Team 상태 전이가 비멱등이다

Team.approve와 reject가 PENDING 상태가 아니면 check로 실패합니다. 이미 승인되거나 거절된 팀을 다시 처리하려 하면 예외가 발생하며, 이는 의도된 상태 전이 보호로 보입니다. 재실행 시 멱등성을 기대하는 호출자에는 영향을 줄 수 있습니다.

제안:

    fun approve() {
        check(status == TeamStatus.PENDING) { "승인 대기 중인 팀만 승인할 수 있습니다." }
        status = TeamStatus.APPROVED
        rejectionReason = null
    }

    fun reject(reason: String) {
        check(status == TeamStatus.PENDING) { "승인 대기 중인 팀만 거절할 수 있습니다." }
        require(reason.isNotBlank()) { "거절 사유는 비어 있을 수 없습니다." }
        status = TeamStatus.REJECTED
        rejectionReason = reason
    }

복수 모델

Comment on lines +51 to +57
---
spring:
config:
activate:
on-profile: local
flyway:
locations: classpath:db/migration,classpath:db/dev

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

경미 local 프로필에만 db/dev 시드가 적용된다

application.yml에 local 프로필 전용 flyway locations이 추가되었습니다. db/dev 디렉터리의 시드 파일이 local 프로필에서만 실행되며, 다른 프로필에는 적용되지 않습니다. 이는 의도된 로컬 전용 시드로 보이나 배포 환경과 스키마 불일치 가능성을 인지해야 합니다.

제안:

---
spring:
  config:
    activate:
      on-profile: local
  flyway:
    locations: classpath:db/migration,classpath:db/dev

복수 모델

@soyuo soyuo self-assigned this Nov 5, 2026
@sandrone-code-review

sandrone-code-review Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Pull Request 요약

AuthorizationBeanConfig에 신규 Bean을 등록하여 인증 설정을 보강했습니다. 팀과 팀원 엔티티를 도입하고 DB 제약으로 팀장·멤버 중복을 방지하며, 해당 서비스·리포지토리에서 멤버십 조회를 제공하고 로컬 테스트용 시드 데이터를 추가했습니다.

복수 모델 · 입력 44,466 · 출력 7,661 · 합계 52,127 토큰

val teamId: String,
@Column(name = "user_id", length = 17, nullable = false)
val userId: String,
role: TeamRole,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

중요 엔티티 필드 중복 정의로 인한 런타임 오류 가능성

Team 엔티티와 마찬가지로 생성자 파라미터와 클래스 몸체에서 role 필드를 중복 정의하고 있습니다. 이는 JPA 엔티티 매핑 시 예상치 못한 동작을 유발할 수 있습니다.

같은 원인의 다른 위치: src/main/kotlin/com/gsmsv/api/team/domain/Team.kt:19-23

제안:

class TeamMember(
    @Id
    @Column(length = 16, nullable = false)
    val id: String,
    @Column(name = "team_id", length = 17, nullable = false)
    val teamId: String,
    @Column(name = "user_id", length = 17, nullable = false)
    val userId: String,
    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = 16)
    var role: TeamRole,
) {

복수 모델

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/pr-review-reply

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

중요: TeamMember 엔티티의 role 필드 중복 정의가 해결되었습니다.
생성자 파라미터를 initialRole로 변경하고, 실제 엔티티 필드는 @Enumerated와 @Column 어노테이션이 적용된 role 프로퍼티로 선언했습니다.
이 구조는 JPA 매핑 시 하나의 컬럼만 사용되므로 런타임 오류 위험이 사라집니다.
추가로 role을 private set으로 제한하고 changeRole 메서드에서만 수정하도록 하여 캡슐화가 강화되었습니다.
다른 파일(Team.kt)에서도 동일 패턴을 적용해 동일 문제가 없도록 확인이 필요합니다.

GPT-OSS 120B · 입력 1,852 · 출력 179 · 합계 2,031 토큰

@soyuo
soyuo merged commit 019bbcc into develop Nov 6, 2026
2 checks passed
@soyuo
soyuo deleted the feat/58-team-entities-membership branch November 6, 2026 01:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(team): 팀·팀원 엔티티와 멤버십 조회

2 participants