본문 바로가기

Architecture/architecture

[Layered] architecture

 

Layered 구조란 어떤 구조일까요? 흔히들 전통적인 구조로 생각하는 Layered구조를 생각해보면 아래의 구조를 떠올리게 됩니다.

말 그대로 겹겹이 쌓여있는 느낌을 주게 되어 Layered라고 불리게 되었습니다.

 

많은 개발자가 개발을 할때 View > Controller > Service > Repository > DB의 구조로 개발을 하곤 합니다.

여기에서 세개의 구조를 볼 수 있습니다.

 

 

1.Presentation(View, Controller)

: View는 사용자 인터페이스(UI) 가 있는 부분으로, 사용자와 직접 상호작용하는 역할을 합니다. 흔히 프론트엔드(React등)등이 이곳에 있습니다.

: Contoller는 사용자가 요청을 하면 그 부분을 받아서 처리하고 view나 model과 상호작용을 합니다.

 

2.Business Logic(Service)

: 시스템의 핵심 로직이 있는 부분입니다.

예를 들면, 주문을 할때 재고를 확인해서 결제를 하고 주문 생성을 하는 등의 행동을 하는 곳이 이곳이라고 생각하면 됩니다. 

 

3.Data Access(Repository)

: 데이터베이스와의 연결을 담당하는 층입니다. 데이터를 CRUD(Create, Read, Update, Delete)하는 역할을 수행합니다.

 


Layered 구조는 어떤 장점이 있을까요?

 

Layered 구조는 명확한 계층 분리를 통해 유지보수성확장성을 높이고, 인터페이스를 활용해 유연한 구조를 만듭니다.

 

1.단순하면서도 명확한 책임 분리

2.높은 응집도, 낮은 결합도

3.유지보수와 확장에 유리한 구조

 

  • 이 Layered 구조를 원활하게 작동하기 위해서 몇몇 규칙들이 존재합니다.

1.단방향 의존성을 유지합니다

: 상위 계층은 하위 계층을 호출하지만, 그 반대는 허용되지 않습니다.

 

2.근접 계층만 사용합니다.

: 상위 계층은 바로 아래 계층만 알면 되며, 하위 전체 구조를 몰라도 됩니다.

 

3.계층 간 영향을 최소화 시킵니다.

: 하위 계층이 변경되어도 상위 계층에 영향을 주지 않도록 설계를 합니다.

 

4.인터페이스 기반으로 설계를 합니다.

: 계층간 통신은 구현체가 아니라 인터페이스를 통해서 이루어집니다. 이를 통해서 약한 결합을 유지할 수 있습니다.


그렇다면 Layered 구조의 한계점은 무엇이 있을까요?

 

1.불필요한 추상화

: 모든 레이어에서 인터페이스를 두다 보면 오히려 코드가 복잡해지고, 실제 구현보다 추상화가 더 많아지는 현상이 생기기도 합니다.

 

// Interface
public interface UserRepository {
    User findById(Long id);
}

// Impl
@Repository
public class UserRepositoryImpl implements UserRepository {
    public User findById(Long id) {
        return new User(id, "홍길동");
    }
}

 

2.계층간 의존성 증가

: 상위 계층이 하위 계층을 의존하기 때문에 하위 계층이 바뀌면 상위 계층도 영향을 받을 수 있습니다.

 

// 기존: JPA
public class UserRepository {
    public User findById(Long id) {
        return entityManager.find(User.class, id);
    }
}

// 변경: 외부 API
public class UserRepository {
    public User findById(Long id) {
        return restTemplate.getForObject("https://api.com/users/" + id, User.class);
    }
}

 

3.유연성이 떨어질 수 있음

: 게층을 무조건 거쳐야 하기 때문에, 단순한 요청도 복잡한 흐름을 따라야 합니다.

: 성능이 중요한 시스템에서는 오히려 지연을 유발할 수 있습니다.

 

@GetMapping("/health")
public String healthCheck() {
    return userService.getHealthStatus();
}

@Service
public class UserService {
    public String getHealthStatus() {
        return "OK"; // DB 안 감
    }
}

 

4.단방향 흐름만으로는 복잡한 요구사항을 처리하는데 어려움

: 이벤트 기반 처리나 크로스커팅 이슈(로깅, 보안 등)에는 불리할 수 있습니다.

(이런 경우에는 hexagonal, event-driven, cqrs등의 아키텍처가 더 적합합니다.)

 

// 하위 계층에서 이메일 전송하고 싶지만...
public class UserRepository {
    public void save(User user) {
        // 저장 완료 후 이메일 발송하려 함
        emailService.sendWelcomeEmail(user); // 🚫 Service를 참조하면 의존성 역전 발생
    }
}

 

'Architecture > architecture' 카테고리의 다른 글

[Hexagonal] architecture  (0) 2025.04.11