Skip to content

Chore/#147: 계층간 의존성 규칙 추가#148

Merged
Darren4641 merged 5 commits intostagingfrom
chore/#147
Feb 21, 2026
Merged

Chore/#147: 계층간 의존성 규칙 추가#148
Darren4641 merged 5 commits intostagingfrom
chore/#147

Conversation

@koosco
Copy link
Member

@koosco koosco commented Feb 20, 2026

summary

  • 계층간 의존성을 명시적으로 추가
  • claude code spotless, test hooks 추가

details

계층간 의존성을 명시적으로 추가

  • archunit을 이용해 계층간 의존성을 테스트로 명시적으로 관리
  • 도메인간 의존 방지

claude code spotless, test hooks 추가

  • claude code 응답이 완료되는 시점에 spotless와 test를 실행하도록 hook 추가

@koosco koosco self-assigned this Feb 20, 2026
@gemini-code-assist
Copy link

Summary of Changes

Hello @koosco, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 Pull Request는 프로젝트의 아키텍처 무결성을 강화하고 개발 워크플로우를 자동화하는 데 중점을 둡니다. ArchUnit을 도입하여 계층 간 및 도메인 간의 의존성 규칙을 코드 레벨에서 강제함으로써, 장기적인 유지보수성과 확장성을 향상시키고 아키텍처의 일관성을 보장합니다. 또한, Claude AI 훅을 통해 코드 포맷팅 및 테스트 실행을 자동화하여 개발 생산성을 높이고 잠재적인 오류를 조기에 발견할 수 있도록 합니다.

Highlights

  • 아키텍처 규칙 정의 및 검증: ArchUnit 라이브러리를 사용하여 계층 간 의존성 및 도메인 격리 규칙을 명시적으로 정의하고 테스트 코드로 검증하도록 추가되었습니다. 이는 클린 아키텍처 원칙을 유지하고 도메인 간 불필요한 결합을 방지하는 데 기여합니다.
  • Claude AI 훅 추가: Claude AI 응답 완료 시 자동으로 gradlew spotlessApply를 실행하여 코드 스타일을 적용하고, gradlew test를 실행하여 테스트를 수행하는 훅이 .claude/settings.json 파일에 추가되었습니다. 이는 코드 품질과 안정성을 자동으로 확보하는 데 도움이 됩니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • .claude/settings.json
    • Claude AI 응답 완료 시 spotlessApplytest 명령을 실행하는 훅이 추가되었습니다.
  • build.gradle.kts
    • ArchUnit 라이브러리(com.tngtech.archunit:archunit-junit5) 의존성이 testImplementation으로 추가되었습니다.
  • src/test/kotlin/com/neki/rule/ArchitectureRulesTest.kt
    • 새로운 테스트 파일이 추가되어 ArchUnit을 활용한 다양한 아키텍처 규칙(레이어 의존성, 인프라 레이어, 도메인 격리, 어노테이션 배치, 포트/어댑터 패턴)을 정의하고 검증합니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link

Code Format Check ✅ PASSED

Spotless Check: success

✨ All code formatting checks passed!


Pushed by: @koosco, Action: pull_request

@github-actions
Copy link

Test ✅ PASSED

Test Result: success

✨ All tests passed!


Pushed by: @koosco, Action: pull_request

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

이번 PR은 ArchUnit을 사용하여 아키텍처 의존성 규칙을 테스트로 명시화하고, Claude Code hook을 추가하여 개발 프로세스를 자동화하는 좋은 변경입니다. 하지만 .claude/settings.json 파일에서 환경 변수가 셸 명령에서 안전하지 않게 사용되어 공격자가 임의 코드를 실행할 수 있는 높은 심각도의 명령 주입 취약점이 발견되었습니다. 이 취약점은 변수를 적절히 살균하거나 인용하여 해결하는 것이 권장됩니다. 또한, 새로 추가된 ArchitectureRulesTest.kt 파일의 코드 중복을 줄이고 유지보수성을 높이기 위한 몇 가지 리팩토링 제안이 있습니다. 전반적으로 훌륭한 기여입니다!

Comment on lines 42 to 54
noClasses()
.that().resideInAnyPackage("..domain..")
.should().dependOnClassesThat().resideInAnyPackage(
"com.neki.auth.api..",
"com.neki.user.api..",
"com.neki.photo.api..",
"com.neki.media.api..",
"com.neki.pose.api..",
"com.neki.map.api..",
"com.neki.term.api..",
"com.neki.version.api..",
).because("Domain layer must not depend on API layer (common.api is allowed)")
.check(importedClasses)

Choose a reason for hiding this comment

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

medium

하드코딩된 패키지 목록은 유지보수를 어렵게 만들 수 있습니다. DomainIsolation 클래스에 정의된 allDomainPackages 리스트를 클래스 레벨로 올리고, 이를 사용하여 동적으로 API 패키지 목록을 생성하는 것을 고려해보세요. 이렇게 하면 도메인이 추가/삭제될 때 한 곳만 수정하면 되어 유지보수성이 향상됩니다.

예시:

// ArchitectureRulesTest 클래스 레벨에 정의
private val allDomainPackages = listOf(
    "com.neki.auth",
    // ... 다른 도메인
)
private val domainApiPackages = allDomainPackages.map { "$it.api.." }.toTypedArray()

// 테스트 내부
noClasses()
    .that().resideInAnyPackage("..domain..")
    .should().dependOnClassesThat().resideInAnyPackage(*domainApiPackages)
    // ...

이 패턴을 Application 계층은 도메인별 API 계층을 의존할 수 없다Infra 계층은 도메인별 API 계층을 의존할 수 없다 테스트에도 동일하게 적용할 수 있습니다.

Comment on lines 152 to 210
@Test
fun `photo 도메인은 다른 도메인에 의존할 수 없다`() {
noClasses()
.that().resideInAnyPackage(
"com.neki.photo.api..",
"com.neki.photo.application..",
"com.neki.photo.domain..",
).should().dependOnClassesThat().resideInAnyPackage(*otherDomainPackages("photo"))
.because("photo domain (api/application/domain) must not depend on other domains")
.check(importedClasses)
}

@Test
fun `media 도메인은 다른 도메인에 의존할 수 없다`() {
noClasses()
.that().resideInAnyPackage(
"com.neki.media.api..",
"com.neki.media.application..",
"com.neki.media.domain..",
).should().dependOnClassesThat().resideInAnyPackage(*otherDomainPackages("media"))
.because("media domain (api/application/domain) must not depend on other domains")
.check(importedClasses)
}

@Test
fun `pose 도메인은 다른 도메인에 의존할 수 없다`() {
noClasses()
.that().resideInAnyPackage(
"com.neki.pose.api..",
"com.neki.pose.application..",
"com.neki.pose.domain..",
).should().dependOnClassesThat().resideInAnyPackage(*otherDomainPackages("pose"))
.because("pose domain (api/application/domain) must not depend on other domains")
.check(importedClasses)
}

@Test
fun `term 도메인은 다른 도메인에 의존할 수 없다`() {
noClasses()
.that().resideInAnyPackage(
"com.neki.term.api..",
"com.neki.term.application..",
"com.neki.term.domain..",
).should().dependOnClassesThat().resideInAnyPackage(*otherDomainPackages("term"))
.because("term domain (api/application/domain) must not depend on other domains")
.check(importedClasses)
}

@Test
fun `version 도메인은 다른 도메인에 의존할 수 없다`() {
noClasses()
.that().resideInAnyPackage(
"com.neki.version.api..",
"com.neki.version.application..",
"com.neki.version.domain..",
).should().dependOnClassesThat().resideInAnyPackage(*otherDomainPackages("version"))
.because("version domain (api/application/domain) must not depend on other domains")
.check(importedClasses)
}

Choose a reason for hiding this comment

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

medium

도메인 격리 규칙을 검증하는 테스트들(photo, media, pose, term, version)이 거의 동일한 구조를 가지고 있습니다. 코드 중복을 줄이고 새로운 도메인을 추가할 때 더 쉽게 관리할 수 있도록 private helper 메서드를 만들어 리팩토링하는 것을 고려해 보세요. 이렇게 하면 규칙 로직이 한 곳에 모이게 되어 유지보수가 용이해집니다.

        @Test
        fun `photo 도메인은 다른 도메인에 의존할 수 없다`() {
            checkDomainIsolation("photo")
        }

        @Test
        fun `media 도메인은 다른 도메인에 의존할 수 없다`() {
            checkDomainIsolation("media")
        }

        @Test
        fun `pose 도메인은 다른 도메인에 의존할 수 없다`() {
            checkDomainIsolation("pose")
        }

        @Test
        fun `term 도메인은 다른 도메인에 의존할 수 없다`() {
            checkDomainIsolation("term")
        }

        @Test
        fun `version 도메인은 다른 도메인에 의존할 수 없다`() {
            checkDomainIsolation("version")
        }

        private fun checkDomainIsolation(domain: String) {
            noClasses()
                .that().resideInAnyPackage(
                    "com.neki.$domain.api..",
                    "com.neki.$domain.application..",
                    "com.neki.$domain.domain..",
                ).should().dependOnClassesThat().resideInAnyPackage(*otherDomainPackages(domain))
                .because("$domain domain (api/application/domain) must not depend on other domains")
                .check(importedClasses)
        }

@github-actions
Copy link

Code Format Check ✅ PASSED

Spotless Check: success

✨ All code formatting checks passed!


Pushed by: @koosco, Action: pull_request

@github-actions
Copy link

Test ✅ PASSED

Test Result: success

✨ All tests passed!


Pushed by: @koosco, Action: pull_request

@github-actions
Copy link

Code Format Check ✅ PASSED

Spotless Check: success

✨ All code formatting checks passed!


Pushed by: @koosco, Action: pull_request

@github-actions
Copy link

Test ✅ PASSED

Test Result: success

✨ All tests passed!


Pushed by: @koosco, Action: pull_request

Copy link
Member

@Darren4641 Darren4641 left a comment

Choose a reason for hiding this comment

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

사용하면 좋은 것 같습니다! 휴먼에러를 줄일 수 있을 것 같아요 LGTM !

@Darren4641 Darren4641 merged commit 2d4064e into staging Feb 21, 2026
2 checks passed
@Darren4641 Darren4641 deleted the chore/#147 branch February 21, 2026 14:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants