회사에서 개발세미나를 하는데,
이번에는 클린아키텍처에 대해서 알려주셔서 정리해서 업로드해본다
들어가며::클린아키텍처란 뭘까?
말 그대로 깔끔한 프로그램 구조이다.
프로그래밍에 있어서 어떻게 소프트웨어를 디자인하고 설계 하느냐?는 개발자의 중요한 역량이다
서비스가 돌아가기 위해서는 다양한 기능들이 필요하고
당연하게도 같은 서비스 내에 있으므로 서로 엮여있다.
a가 있어야만 b를 만들 수 있는 상황
또는 a의 정보에 따라 b의 형태가 바뀌는 상황 등등.
모델과 참조란?
서비스를 만들 땐, "Model"이라는 개념이 필요한데, 이건 현실세계에 있는 개념을 프로그래밍 세계로 옮겨온 거라고 생각하면된다.
예를 들면
"혜윤"모델이 있다.(나다. 현실세계에 있는 개념)
그리고 여기에는 이름,나이,성별,기능.. 등이 들어갈 수 있겠지?
MODEL HYEYOON {
name
age
gender
sayHi()
}
그걸 정의해놔야지 이 모델을 가지고 와리가리를 치면서 (ㅎㅎ) 서비스를 만들어낼 수 있다.
바로 위에서
"a가 있어야만 b를 만들 수 있는 상황
또는 a의 정보에 따라 b의 형태가 바뀌는 상황 등등." 을 언급했는데, 이걸 프로그래밍에서는 참조한다고 부른다.
(참조라는 말은 아주 넓은 범위에서 쓰인다.)
상호참조 vs 일방향 참조
참조에는 두가지 종류가 있는데,
주문과 사용자. 두가지 모델을 통해서 참조의 예시를 보자..
사용자가 쇼핑몰에 들어가서 주문이라는 행동을 하는 상황이다.
상호참조 (A ↔ B)
이름만 들어도 서로가 서로를 참고하면서 기능을 수행할 것만 같다.
=>User 안에 Order를 들고 있고, Order 안에도 User를 들고 있는 상황
예를들면..
user를 정의할때 주문이라는 속성이 있어야하고, 주문이라는 속성이 변화하면 사용자 모델링또한 바뀌어야한다.
반대도 마찬가지.
일방향 참조 (A → B)
ORDER를 정의할때 USER이라는 속성이 있어야하고, 그렇기 때문에 사용자 모델링에 변화가 생기면
주문 모델링또한 바뀌어야한다.
그런데 "클린아키텍처"구조를 위해서는 참조를 최소화 해야한다.
왜? 유지보수 때문에!
참조가 많아질수록 도메인 독립성이 깨진다..
예를들면 User 스키마 바꾸면 Order 로직도 깨질 수 있다.
결국 속도가 느려지고 유지보수 비용 증가한다.
예쁜 디자인을 위해서는 A가 바뀌면 B,C까지 바뀌어야하는 상황을 피하는게 좋다.
좋지않은 방식의 예시 (User ↔ Order)
// Order가 User 자체를 직접 참조
class Order {
id: string;
user: User; // ❌ 강한 의존
}
참조를 어떻게 피하나요?
아래와 같은 두가지 방법을 쓴다.
1.엔티티끼리 직접 붙이지 말고 id 같은 식별자 수준으로만 연결
2.인터페이스(추상화)를 두기 → 실제 구현(DB, API 등)은 모르게 하고, 서비스/유스케이스 계층에서만 의존
개선된 방식 (약결합 + 인터페이스)
1,2번방법은 함께 쓸순 있겠지만 연결되는 내용이 아닌 별개이다.
<식별자를 통해 참조하는 예시>
// Order는 User를 몰라도 됨
class Order {
id: string;
userId: string; // 식별자만
items: string[];
}
인터페이스
실제 구현(DB, API 등)은 모르게 하고, 서비스/유스케이스 계층에서만 의존
개선된 방식 (약결합 + 인터페이스)
참고) 사실상 프로그래밍에서 인터페이스는, 본문에 언급한 것보다 더 넓은 범위를 커버하는 개념임을 참고해주시길 바란다.
나는 인터페이스를 두가지 모델의 연결고리 같은 느낌으로 이해했다.
또한, 인터페이스는 일반적으로 MVC모델 기준 V,C 파트에서 구현해서 사용한다.
(위의 엔티티가 M model 파트에 해당하겠지!)
아래는 인터페이스 구현의 예시이다. 유저레포지토리에서 findbyid라는 것을 가지고 있긴한데,(DB검색기능)
어떻게 할지는 구체화 되어있지 않다.(참고) 레포지토리라 함은 보통 DB와 소통하는 계층을 통틀어 이야기 한다.
)
이름만 findById 되어있지 내용이 없다.
interface UserRepository {
findById(id: string): Promise<User | null>;
}
아래의 코드 블락은 userRepository를 이어받되, 구현은 자기가 한다.
class DBUserRepository implements UserRepository {
findById(id: string) {
return "DB에서 찾은 유저";
}
}
class CacheUserRepository implements UserRepository {
findById(id: string) {
return "캐시에서 찾은 유저";
}
}
return 을 실제로 정의해놓은게 보이시는가 ? (구현체)
정리하자면
인터페이스 = 계약(형식만 정의)
구현체 = 실제 동작 (DB 연결, API 호출 등)이다.
그러니까 참조처럼 실제로 연결된 서비스의 구체적 구현을.. 안하고
아 이런게 있다.근데 구체적인 내부 구현은 몰라~ 요렇게 놔둠으로써
들고 있는 서비스가 바뀌었을때 모르쇠 할 수 있는거다. (독립성 유지)
마치며::클린아키텍처의 첫걸음:참조없는 디자인하기
오늘은 참조가 뭔지, 참조를 막는 방법 두가지 식별자 ID 를 이용하는 방법과 인터페이스를 이용하는 방법에 대해 알아보았다~
'클린아키텍처' 카테고리의 다른 글
| [클린 아키텍처]왜 같은 객체를 여러 레이어에 각각 따로 둘까? (0) | 2026.03.07 |
|---|---|
| DI 의존성 주입 구조 (1) | 2026.01.16 |
| 의존성 주입(Dependency Injection)이란? (0) | 2025.11.09 |
| [클린아키텍처] MVC가 먼저인가 DDD가 먼저인가? (+유즈케이스란?) (0) | 2025.10.03 |