생각 정리...

나만의 생각 흐름 정리 : 의존성 문제에서 MSA까지

JuNo_12 2025. 6. 21. 20:48

들어가며

제가 작성한 모든 개념들을 다 이해하실 필요 없습니다. 제가 이 게시글을 작성한 이유는 여러가지 개념들을 정리하고싶어서가 아니라 어떤식으로 제가 앞으로 나아갈 수 있었는지에 대한 제 생각의 흐름을 보여주고싶었습니다.

우선 SOLID 원칙에 대해 알아보겠습니다. 한번씩만 읽어보시고 진행해주세요. 아직 이해를 못하셔도 됩니다. 지금은 이 SOLID 원칙이 그저 별거 아닌거같아보이겠지만, 결국 어떠한 문제를 발견하고 해결해야하는 과정이 전부 SOLID 원칙을 관통합니다. 이 부분에 대해서는 뒤에서 자세히 다루도록 하겠습니다.

 

SOLID 원칙 상세 설명:
1. 단일 책임 원칙 (SRP - Single Responsibility Principle):
"클래스는 단 하나의 변경 이유만을 가져야 한다." 
즉, 클래스는 하나의 기능 또는 책임만을 담당해야 합니다. 이렇게 하면 클래스가 변경될 이유가 단일해지고, 변경으로 인한 파급 효과를 최소화할 수 있습니다. 


2. 개방/폐쇄 원칙 (OCP - Open/Closed Principle):
"소프트웨어 구성 요소는 확장에 대해 열려 있어야 하지만, 변경에 대해서는 닫혀 있어야 한다." 
즉, 기존 코드를 변경하지 않고도 새로운 기능 추가가 가능해야 합니다. 이를 위해 추상화와 다형성을 활용합니다. 


3. 리스코프 치환 원칙 (LSP - Liskov Substitution Principle):
"자식 클래스는 언제나 자신의 기반 (부모) 클래스를 대체할 수 있어야 한다." 
즉, 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 교체해도 프로그램이 정상적으로 동작해야 합니다. 


4. 인터페이스 분리 원칙 (ISP - Interface Segregation Principle):
"클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다." 
즉, 큰 인터페이스보다는 특정 클라이언트에게 필요한 인터페이스만 제공하는 것이 좋습니다. 이를 통해 불필요한 의존성을 줄이고, 변경에 대한 영향을 최소화할 수 있습니다. 


5. 의존 관계 역전 원칙 (DIP - Dependency Inversion Principle):
"고수준 모듈은 저수준 모듈에 의존해서는 안됩니다. 둘 다 추상화에 의존해야 합니다." 또한, "추상화는 세부 사항에 의존해서는 안 됩니다. 세부 사항은 추상화에 의존해야 합니다." 
즉, 구체적인 구현 클래스보다는 추상화된 인터페이스나 클래스에 의존해야 합니다. 이를 통해 코드의 유연성과 재사용성을 높일 수 있습니다. 


처음으로 문제를 마주했던 상황

제가 가장 먼저 문제를 마주했던 상황에 대해 설명하겠습니다.이는 스프링을 배우면서 진행했던 첫 팀프로젝트 얘기입니다. 


저번 팀프로젝트에서 논리적 삭제를 구현하기 위해서 User 도메인에서 Todo 서비스와 Comment 서비스에 대한 논리적 삭제 메서드를 호출해서 로직을 작성했는데, 이때 다른 도메인의 팀원이 저 메서드를 만들때까지 기다려야하는 문제점을 발견했습니다.

 

즉, 이는 개발 생산성과 연관되는 '독립적인 개발' 이 불가능하다는 큰 문제이죠. 이를 해결하기 위해 여러 튜터님들을 찾아가기도하고 구글링을 함으로써 이를 해결하기 위한 해결책을 몇가지 찾았습니다.

 

1. 인터페이스를 통한 의존성 역전

2. 스프링 이벤트를 통한 이벤트 기반 아키텍처

 

자, 이 두가지의 해결책이 뭘 의미하냐? 제가 겪은 도메인 간 의존성 문제가 바로 DIP 위반 사례라는 것입니다.

저는 여기서 '의존성' 이라는 것의 문제점을 인지했고, 이를 해결하기 위한 아키텍처에 대해 고민해봤습니다.


의존성을 끊는 구조적 접근법들

 의존성을 끊는 아키텍처는 굉장히 많습니다. 이미 제가 고안했던 문제에 대한 해결책을 이전의 개발자분들이 고민하여 만들어놨죠. 간단하게 어떠한 아키텍처가 있는지 알아보겠습니다. 그냥 눈으로만 보시고 이해는 안하셔도됩니다.

 

1. 헥사고날 아키텍처 (Ports & Adapters)

Domain (핵심 비즈니스 로직)
    ↕ (Port/Interface)
Adapter (외부 세계와의 연결점)

 

2. 클린 아키텍처

Entities → Use Cases → Interface Adapters → Frameworks & Drivers
(안쪽으로만 의존성 방향)

 

3. 마이크로서비스 아키텍처

  • 서비스 간 완전한 독립성
  • API 통신으로만 상호작용

 

4. 이벤트 기반 아키텍처 (Event-Driven)

  • 이벤트를 통한 느슨한 결합
  • 발행자는 구독자를 몰라도 됨

 

5. DDD (Domain-Driven Design)

  • Bounded Context로 도메인 분리
  • Aggregate 경계 설정

 

의존성을 끊는 아키텍처는 굉장히 다양한 것을 알 수 있습니다.

제가 참고했던 유튜브 링크를 함께 올릴테니, 여유가되실 때 이를 보시면 더욱 이해가 잘 될겁니다.

 

스프링이벤트 : https://www.youtube.com/watch?v=TJUIkLFpgGo

DDD (도메인 주도 설계) : https://www.youtube.com/watch?v=sLG5n_pXWK0&t=1s

 

그리고 실제로는 단일 아키텍처 패턴만 사용하는 것이 아니라 여러 패턴을 조합해서 사용합니다.

저희 팀에서 진행했던 아웃소싱 프로젝트도 마찬가지죠.

 

이벤트 기반 + DDD

// Auth 도메인에서 이벤트 발행
eventPublisher.publishEvent(new UserRegisteredEvent(...));

// User 도메인에서 독립적으로 처리
@EventListener
public void handleUserRegistered(UserRegisteredEvent event) {
    // Bounded Context 내에서만 처리
}

 

레이어드 아키텍처 + 헥사고날의 아이디어

Controller (Interface Adapter)
    ↓
Service (Use Case)
    ↓
Repository (Port) → JPA Implementation (Adapter)

 

클린 아키텍처의 의존성 방향 + 이벤트 기반

  • 내부 도메인은 외부를 모름
  • 이벤트로 도메인 간 통신

 

마이크로서비스적 사고 (모놀리스 내에서)

  • Auth, User, Task, Comment 각각 독립적 도메인
  • 각자의 Repository, Service, Exception Handler

 

이 세상에 완벽한 코드란 존재하지 않습니다. 상황에 따라, 혹은 팀원들의 성격에 따라 등 굉장히 다양한 코드가 나옵니다.


코드를 구현할 때도 굉장히 많은 선택지를 거칩니다. 저희는 이러한 선택지에서 자신만의 '기준' 을 세워서 '선택' 을 해야합니다.


결코 쉬운 것이 아닙니다. 하지만 적어도 자신이 생각하는 이상적인 코드, 즉 '좋은 코드' 에 대한 깊은 고민이 필요합니다.

아래 글은 제가 저만의 기준을 잡으려고 노력했던 것에 대한 정리글입니다.

https://juno0112.tistory.com/65

 

[트러블슈팅] Spring Boot JWT 인증 시스템 : 나만의 설계 기준을 잡아보자.

들어가며며JWT 인증을 순수 Filter + ArgumentResolver 방식으로 구현한 코드를 보니 다음과 같은 수정 포인트들이 눈에 보였습니다. 아직 제가 컨트롤러 이전에 작동되는 부분에서 어떠한 구조로 설계

juno0112.tistory.com


좋은 코드? 그게 뭐지...

우선 저만의 '좋은 코드'의 기준을 알려드리겠습니다.

 

1. 확장성 우선 설계 → OCP (Open/Closed Principle)

"지금 돌아가게만 만들지 말고, 나중에 기능이 추가될 때도 쉽게 확장할 수 있게"

 

2. 도메인 독립성   DIP (Dependency Inversion Principle) + SRP (Single Responsibility Principle)

"다른 팀이 코드를 바꿔도 내 도메인은 영향받지 않아야 함"
: 구체적인 것에 의존하지 말고 추상화에 의존

 

사실 전부 SOLID 원칙에 적혀있던 얘기였습니다.

여러분들도 자신만의 기준을 세우는 시간을 가졌으면 좋겠습니다.

 

자 이제 여기서 드는 의문이 있을거같습니다.

"아 그래 일단 뭐 좋은코드고 뭐고 일단 다 알겠고, 그래서 어떻게 해야하는건데?" 라는 의문.

 

이해합니다. 이게 막상 시작하려니까 막막합니다. 저도 정말 너무 막막했습니다.

 

그래서 현재 제가 생각했을 때의 가장 이상적인 아키텍처에 대해 먼저 공부해봤습니다.


내가 생각한 가장 이상적인 구조 : MSA

튜터님과 얘기를 해보면서 MSA 라는 단어에 대해 처음 들어봤습니다. 그래서 뭔가 "아 지금 이 단어가 내가 현재 느끼는 문제점을 해결해 줄 수 있는 키워드구나." 라고 생각을 하여 무작정 이 단어에 대해 공부하기 시작했습니다.

아래는 제가 작성한 게시글입니다.

https://juno0112.tistory.com/73

 

[트러블슈팅] 인증 시스템 설계 - 모놀리스에서 MSA까지

안녕하세요! 백엔드 스프링 캠프에서 공부하고 있는 학생입니다.인증 시스템을 자세히 다뤄보다가 어떻게 하면 각 도메인들이 REST한 API를 구현할 수 있을지 고민해봤습니다.특히 실무에서도 바

juno0112.tistory.com

 

공부를 하고나니, MSA의 매력적인 점들이 눈에 보였습니다.

 

MSA의 매력적인 점들

1. 완전한 독립성

  • 각 서비스가 완전히 분리됨
  • 다른 팀의 코드 변경이 내 서비스에 영향 X

 

2. 확장성

  • 트래픽 많은 서비스만 스케일 아웃
  • 기술 스택도 서비스별로 다르게 선택 가능

 

3. 팀 독립성 

  • 각 팀이 자신의 서비스만 책임
  • 배포도 독립적으로

 

이렇게 글만 보면 굉장히 이해하기 힘듭니다. 저도 이 MSA를 다 이해하지는 못했습니다. 하지만 적어도 왜 이러한 아키텍처가 존재하고, 이 아키텍처의 장점과 단점에 대해서는 명확하게 알고있습니다. 그래서 여러분들도 이 MSA의 구조에 대해 한번씩은 깊게 공부해보셨으면 좋겠습니다. 그리고 추후에 다룰 Docker의 컨테이너와도 굉장히 닮아있습니다.

아래는 제가 작성한 Docker에 대한 정리글입니다.


https://juno0112.tistory.com/84

 

Docker? 그게 도대체 뭔데?

프롤로그: Docker를 마주하다Spring Boot 기반의 TaskFlow 프로젝트를 진행하던 중, Docker라는 기술을 접하게 되었습니다. "Docker? 그게 뭔데?"라는 궁금증에서 시작된 학습 여정을 정리해보려 합니다.첫

juno0112.tistory.com

 


마치며

저는 순간순간 드는 의문, 다른 분이 작성한 코드에 대한 생각/의도, 처음 듣는 개념 등 간단하게라도 바로 메모장에 적어놓습니다. 그리고 이를 하나하나 블로그를 작성하면서 메모장에 적힌 내용들을 하나씩 소거합니다. 그러다보면 처음에는 보이지 않던, 어떻게 접근을 해야할지 몰랐던 점들이 한 순간 머릿속에 정리가 되는 순간이 옵니다. 

저는 개인적으로 컴퓨터 공학뿐만 아니라 다른 공학 계열에서도 실력은 계단식으로 오른다고 생각합니다. 계속 공부를 해도 실력이 안느는거 같아보이지만, 꾸준히 하다보면 어느순간 시야가 넓어집니다. 그래서 다들 잘하고 있으니까 포기하지말고 하루하루 열심히 앞으로 나아갔으면 좋겠습니다.

 

제가 우연히 본 문장이 있습니다. "나는 어제의 나보다 덜 바보가 되어간다." ... 굉장히 공감했습니다. 
긴 글 읽어주셔서 감사합니다. 궁금한 점은 절 찾아오시거나 댓글로 작성해주시면 감사하겠습니다.