GDSC Sookmyung 활동/10 min Seminar

Clean Architecture

gomgom 2023. 4. 2. 00:00

안녕하세요! 저는 오늘 Clean Architecture라는 주제로 10 세미나 하려고 합니다.

 

제가 클린 아키텍처에 관심을 갖게 저번 학기부터였습니다. 저번학기에 컴퓨터공학전공 수업인 컴퓨터특강 수업을 들었는데 수업의 주제가 클린 코드였습니다. 그래서 사실 코딩이나 알고리즘 공부를 하면서 생각해보지 않았던 클린 코드에 대해 깊게 생각해보기 시작했습니다. 그리고 현재 토비의 스프링이라는 책으로 GDSC 스프링 심화 스터디를 하고 있는데 아직 전체를 읽어보지는 못했지만 클린 아키텍처가 얼마나 중요한지에 대해 느끼게 되어 이와 같은 주제로 발표를 준비하게 되었습니다.

 

먼저 클린 아키텍처란 무엇일까요? 

 전에 아키텍처의 목적부터 알아야 합니다. 모든 아키텍처의 목적은 관심사의 분리라고   있는데요, 각각의 관심사를 계층별로 분리하여  계층이 하는 역할을 명확하게 하는 것이라고   있습니다. 클린 아키텍처의 목적을 5가지로 정리해봤습니다. 첫째, 프레임워크와 독립적이어야 한다. 둘째, 테스트에 용이해야 한다. 셋째 UI 독립적이어야 한다. 넷째, DB 독립적이어야 한다. 마지막으로 외부 기능과 독립적이어야 한다. 5가지로 정리하긴 했지만 여러분도 느끼셨듯이 결국은 독립적인 기능으로 작동해야 한다 것입니다.

 

그럼 본격적으로 클린 아키텍처에 대해 알아볼까요? 먼저 위의 그림을 보면 화살표를 찾을 있으실 텐데요, 화살표는 의존성을 의미합니다. 화살표의 방향이 바깥쪽에서 안쪽으로 향하는 것을 보아 클린 아키텍처의 의존 규칙은 안쪽을 향해야 한다 것을 있습니다. 이처럼 안쪽 원은 바깥쪽 원에 대해 없어야 하고 마찬가지로 바깥쪽 원에서 사용하는 데이터 포맷도 안쪽 원에서는 사용하지 않아야 합니다. 한마디로 원의 안쪽은 바깥과는 무관하게 역할을 수행해야 한다 것입니다. 또한 원의 바깥, 경계의 바깥으로 갈수록 중요하고 언제든지 바뀔 있는 세부사항으로 취급하고, 안쪽으로 갈수록 세부적이고 고수준으로 표현되며 변경에 대해 닫혀있어야 합니다.

고수준과 저수준이 조금 헷갈리지 않으신가요? 그래서 예시를 준비했습니다. 고수준은 보다 추상화된 개념이므로 밥을 먹는다라고 한다면 저수준은 추상화된 개념을 실제 어떻게 구현할지에 대한 세부적인 개념이므로 숙대 맛집 포돈에서 밥을 먹는다라고 있습니다.

 

앞에서 경계라는 단어를 한번 언급했었는데요, 경계는 클린 아키텍처에서 중요하게 생각해야할 하나입니다. 위의 그림처럼 계층이 명확하게 분리가 되어 있는데 그렇다면 경계 간에 데이터를 전달할 무엇을 전달해야 할까요? 의존성 규칙을 지키기 위해서는 우리가 사용하는 단순하고, 고립된 형태의 데이터 구조인 entity 형태를 사용하는 것을 추천합니다. 뿐만 아니라 경계를 넘나드는 데이터는 항상 안쪽 원에서 사용하는 데이터 형태여야 합니다. 개발자라면 한번쯤은 읽어봐야할 클린 코드 책의 저자인 로버트 마틴은 경계에 대해서 소프트웨어 아키텍처는 선을 긋는 기술이라고 말하며 경계는 소프트웨어 요소를 서로 분리하고 경계 한편에 있는 요소가 반대편에 있는 요소를 알지 못하도록 막는다고 했습니다.

 

이제 클린 아키텍쳐를 하나씩 뜯어보도록 하겠습니다.

먼저 원의 가장 안쪽에 있는 엔티티입니다. 엔티티는 가장 중요한 핵심이 되는 부분으로 핵심 업무 규칙을 캡슐화합니다. 메소드를 가지는 객체로 일련의 데이터 구조와 함수의 집합이며 가장 변화하지 않고 외부로부터 영향을 받지 않습니다.

 

유즈케이스 애플리케이션의 특화된 업무 규칙을 포함합니다. , 애플리케이션의 비즈니스 규칙을 캡슐화합니다. 해당 계층의 변경은 엔티티 계층에 영향을 주어선 안되며 UI, 프레임워크 등의 변경이 해당 계층으로 영향을 주어선 안됩니다. 애플리케이션의 동작이 변경되었을 해당 계층이 영향을 받습니다. 해당 계층은 엔티티의 비즈니스 로직을 애플리케이션의 동작에 따라 실행합니다. , 엔티티로 들어오고 나가는 데이터 흐름을 조정하고 조작합니다.

 

인터페이스 어댑터 쉽게 말해 엔티티, 유즈 케이스 계층이 다루기 편한 데이터 포맷에서 UI, DB 다루기 편한 데이터 포맷으로 바꿔주는 역할을 합니다. 일련의 어댑터들로 구성되어 있으며 내외부에서 처리하기 편한 방식으로 변환합니다. 컨트롤러, 게이트웨이, 프레젠터가 여기에 속합니다.

 

마지막으로 프레임워크와 드라이버입니다. 가장 외부에 위치하여 안쪽 원에 영향을 주지 않으며 시스템의 핵심 업무와는 관련 없는 세부사항입니다. 프레임워크, 데이터베이스, 서버 등이 여기에 속합니다.

 

이제 클린 아키텍처를 스프링 부트에 적용시켜봅시다. 가장 많이 하는 게시판을 JPA를 사용해서 구현한다고 가정해보겠습니다.

만약에 글을 작성하는 Post 클래스에서 2시간에 1개의 뷰 올라가던 동작을 1시간마다 1개의 뷰 올라가는 동작으로 바꾸고 싶다면 코드 변경이 필요할까요? 이렇게 동작이 바뀐다 해도 코드에서 바뀌는 점은 없습니다. , 시나리오의 변경에 따라 해당 글의 메소드가 바뀌는 부분이 없기에 Post 클래스는 엔티티에 해당합니다. 이러한 엔티티를 중심으로 유즈케이스를 구현하면 됩니다. 유스케이스 별로 모델을 만들면 해당 유스케이스를 명확하게 이해할 수 있고 장기적으로도 유지보수에 도움이 됩니다. 인터페이스 어댑터는 JPA를 사용하기 위해 테이블 클래스를 만드는 것과 같이 특정한 포맷으로 변경하는 역할을 합니다. 만약 JPA 아닌 다른 ORM 사용한다면 코드를 바꿔야할까요? 아닙니다. 이미 만들어 놓은 인터페이스를 상속하는 클래스를 구현하여 유즈케이스의 변경 없이 사용이 가능합니다.

 

이렇게 클린 아키텍처에 대해 알고 나니 확실히 클린한 코드, 클린한 아키텍처로 코드를 짜고 싶다는 생각이 들었습니다. 그래서 백준 문제를 때도 이를 적용하고 싶었고 적용한 저의 경험을 공유하려고 합니다.

여기 계신 모든 분들이 아주 간단하게 있는 사칙연산 코드입니다. 메인 코드를 보면 3개의 코드가 모두 동일하고 calculate 반환 값만 바뀌었다는 것을 있습니다. 아주 간단한 변경으로 3문제를 있다니 너무 간편하지 않나요?

 

하나의 예시를 가져왔는데 해당 문제는 바구니 뒤집기(백준 10811번) 문제와 뒤집기(백준 10813) 문제입니다. 바구니를 뒤집는 방법과 공을 뒤집는 방법은 다르지만 입력과 출력부분, 문제를 풀기 위해 초기 세팅을 하는 부분이 모두 같기에 왼쪽 코드의 reverseBasket함수와 오른쪽 코드의 swapBall함수만 다를 모든 코드가 동일합니다. 추후에 바구니를 뒤집는 방법이 바뀐다면 우리는 reverseBasket 함수의 내용만 바꿀 있으므로 유지보수도 쉽고, 코드의 어느 부분을 고쳐야 하는지도 쉽게 파악할 있습니다.

 

마지막으로 클린 아키텍처의 장점을 한페이지로 정리해봤습니다.

내부 레이어가 사용자 인터페이스와 다른 외부 관련 요소와 독립되어 있어 책임이 명확하다는 점에서 비교적 빠르게 코드 파악이 가능합니다. 그리고 클래스 집중화로 테스트와 유지보수에 용이하며 이슈발생시 예측도 쉽게 있습니다. 마지막으로 새로운 기능이 추가되거나 내부 로직이 변경될 유연하게 대처가 가능하다는 장점이 있습니다.

 

앞으로는 클린 아키텍처, 클린 코드도 고려해서 개발해보는건 어떠신가요? 감사합니다!

 

Reference