웹 애플리케이션을 개발할 때 가장 중요한 것은 코드의 구조화다.
잘 구조화된 코드는 유지보수가 쉽고, 확장성이 좋으며, 테스트하기 용이하다.
이번 포스팅에서는 스프링의 MVC 패턴과 DAL을 통해 효율적인 계층형 아키텍처를 구현하는 방법을 알아보자 👀
웹 애플리케이션은 일반적으로 여러 계층으로 나뉘어 설계된다.
각 계층은 고유한 책임을 가지며, 다른 계층과 독립적으로 동작한다.
여기서 주요 계층 구조는 다음과 같다.
주요 계층 구조
Presentation Layer (View, Controller)
↓
Business Layer (Service)
↓
Data Access Layer (Repository, Mapper)
↓
Database
백엔드에서 주로 사용되는 계층구조이다.
여기서 View, Controller, Service, Repository 등의 구성 요소가 존재하며, SpringBoot는 이러한 계층 구조를 지원하고 이를 Spring MVC라고 한다.
이전에도 언급했다시피 여러 계층으로 나누어서 설계하면 책임을 분리하기 용이하다.
이러한 책임 분리는 코드의 재사용성 향상, 유지보수의 용이성, 테스트 용이성이라는 장점을 가져온다!
실제로 SpringBoot를 활용한 미니 프로젝트를 진행해보니 어떤 의미를 갖는지 한번에 이해가 되는 것 같다.
(이 과정을 어떻게 포스팅해야할지는 조금 고민된다..)
MVC는 Model-View-Controller의 약자로, 애플리케이션을 크게 세 가지 역할로 구분한 디자인 패턴이다.
우선 MVC에서 각 철자가 어떤 의미를 갖는지부터 설명하고 시작하려고 한다.
MVC 패턴에서 Model이란 어플리케이션에서 사용할 데이터를 의미한다.
우리가 이해해야하는 것은 이 데이터는 데이터의 구조와 비즈니스 로직을 정의할 때 사용할 수 있다는 것이다.
Model은 크게 3가지로 분류할 수 있다.
1. Domain Object
- 어플리케이션의 주요 비즈니스 개념을 나타내는 객체
- ex) 사용자를 나타내는 User 클래스
- 일반적으로 엔티티(Entity)라고도 하며, 데이터베이스의 테이블과 매핑된다.
2. Form Object
- 사용자의 입력을 받아서 저장하는 객체
- 주로 웹 어플리케이션에서 폼 데이터를 캡처하고 검증하는데 사용한다.
- 도메인 객체와 비슷하지만 주로 사용자 인터페이스와 상호작용하는데 사용된다.
3. DTO (Data Transfer Object)
- 계층 간 데이터를 전달하는데 사용하는 객체
- 주로 Service Layer와 Presentation Layer 사이 데이터 전송에 사용된다.
- 도메인 객체와 구분하여 사용되며, 필요한 데이터만 포함하도록 설계한다
필자는 이러한 개념을 크게 짚어두고 미니 프로젝트를 진행하진 않았다.
하지만, 지금 정리하면서보니 "아 이게 그거구나"하는 느낌으로 이해가 되고있다.
그러니 위 내용이 이해가 되지 않는다면 우선 읽고 넘기도록 하자!
View는 사용자에게 데이터를 표시하고 사용자 인터페이스를 제공하는 역할을 수행한다.
JSP, Thymeleaf, FreeMarker, Velocirty와 같은 템플릿 엔진을 사용하는데 필자는 여기서 Thymeleaf를 사용해서 진행한 미니 프로젝트를 포스팅으로 정리할 예정이다.
만약 REST API로 백엔드 서버를 구축한다면 View라는 개념은 사라지게 된다.
왜냐하면, API를 호출하는 프론트엔드에서 사용자 인터페이스를 제공하기 때문이다.
사용자 요청을 처리하고 적절한 응답을 생성하는 역할을 바로 Controller가 수행한다.
즉, 사용자가 요청한 URL을 매핑하여 해당 요청을 처리하는 메서드를 호출하고, 모델 데이터를 생성하거나 수정한 후에 적절한 뷰를 선택하여 응답을 반환한다.
조금 더 자세히 살펴보면 주요 역할은 다음과 같다.
1. Request Mapping
- URL 경로와 Http 메서드 (GET, POST, PUT, DELETE 등)를 매핑하여 해당 요청을 처리하는 메서드를 지정
@RequestMapping
,@GetMapping
,@PostMapping
,@PutMapping
,@DeleteMapping
등의 어노테이션을 사용
2. Model Handling
- 모델 객체를 사용하여 데이터를 뷰에 전달
- Model, ModelMap, ModelAndView를 사용하여 모델 데이터를 처리한다.
3. View Selection
- 적절한 뷰 이름을 반환하며, View Resolver가 이를 실제 뷰로 매핑하고 렌더링한다.
4. Form Handling
- 사용자 입력 폼 데이터를 처리하고 검증한다.
@ModelAttribute
,@Valid
어노테이션을 사용하여 폼 데이터를 바인딩하고 검증한다.
우선 이번 포스팅에서는 개념적인 부분을 정리할 예정이다.
다음 포스팅에서 개념과 예제 코드를 매핑시키면서 설명하려고 한다.
DAL은 간단하게 데이터베이스와의 상호작용을 담당하는 계층이다.
이 계층을 통해 비즈니스 로직과 데이터 접근 로직을 분리할 수 있다.
이전까지 설명한 계층 구조를 한번 살펴보자
여기서 빨간색으로 표시된 박스 부분이 바로 데이터 접근 계층 (DAL)이다.
여기서 우리는 DAO라는 개념을 사용하는데, 이는 서비스와 DB를 연결하기 위해 사용한다.
위 사진에서 DAO를 없애보자
여기서 DAO는 Data Access Layer이다
사진을 통해서도 추론할 수 있듯이, DAL이 없다면 Service(비즈니스 계층)에서 DB에 연결하는 로직과 DB를 조작하는 코드들이 같이 구현되어야 한다.
만약, 회원 가입을 수행하는 서비스를 제공한다면 회원 가입 로직보다 DB에 접근하는 로직이 더 길어지는 문제가 발생할 수 있다!
따라서, 우리는 DAO를 구현하여 DB와 연결하는 로직과 쿼리를 만들고 결과를 반환하는 로직을 분리할 수 있다.
그렇다면 여기서 DAO는 정확하게 무엇을 의미할까?
여기서 DAO는 DB와 상호작용을 담당하는 객체를 의미한다.
일반적으로 DAL를 추가하면 다음과 같은 방식으로 DB와 상호작용을 수행한다.
어플리케이션 <--> DB 상호작용
어플리케이션의 Object <- SQL을 이용한 CRUD -> DB의 Record 단위 처리
즉, 개발자는 SQL문을 이용해서 Object와 Record 사이의 불일치를 해결해야 한다.
간단한 코드를 한번 살펴보자
Sample Code
@Repository public class UserDao { @Autowired private JdbcTemplate jdbcTemplate; public void insertUser(User user) { // Object -> Record 변환 String sql = "INSERT INTO users (username, email) VALUES (?, ?)"; jdbcTemplate.update(sql, user.getUsername(), user.getEmail()); } public User selectUser(Long id) { // Record -> Object 변환 String sql = "SELECT * FROM users WHERE id = ?"; return jdbcTemplate.queryForObject(sql, new Object[]{id}, (rs, rowNum) -> new User( rs.getLong("id"), rs.getString("username"), rs.getString("email") )); } }
UserDao는 용도에 맞게 SQL 쿼리를 내부에서 미리 생성하고 DB와 상호작용을 수행한다.
이처럼 Dao를 사용한다면, 비즈니스 로직과 데이터 접근 로직을 명확하게 분리하여 데이터베이스 연산을 객체 지향적으로 처리할 수 있다!
이번 포스팅에서는 Spring에서 사용하는 MVC의 개념과 DAL이라는 Database 접근 계층에 대해서 조금 정리해봤다.
아직 DAO를 사용한 실습을 해보진 않았으나 어떤 목적으로 사용하는지는 분명 이해가 된다.
다음 포스팅부터는 필자가 진행한 미니 프로젝트를 조금씩 정리하며 개념을 되짚어보려고 한다 👊