부스트코스 웹 프로그래밍 BE 영상을 보며 공부한 것을 간단히 정리한다.
1. Spring MVC
Spring MVC 란
- Spring 프레임워크에서 제공하는 웹 모듈이다.
- MVC 는 Model-View-Controller 의 약자로, 기본 시스템 모듈을 MVC 로 나누어 구현되어있다.
- Model 은 '데이터' 디자인을 담당한다.
- ex. 상품 목록, 주문 내역 등
- View 는 '실제로 렌더링되어 보이는 페이지' 를 담당한다.
- ex.
.JSP
파일들이 여기에 해당된다.
- ex.
- Controller 는 사용자의 요청을 받고, 응답을 주는 로직을 담당한다.
- ex. GET 등의 uri 매핑이 여기에 해당된다.
- Model 은 '데이터' 디자인을 담당한다.
- Spring MVC 모듈을 사용하여, 백엔드 프로그래밍의 기본 프레임워크를 잡는다.
- Web 서버에 특화되어 만들어진 모듈이라, 개발자가 해야할 영역을 더 적게 만들어준다.
- 즉 기존에 Spring 보다 더 깔끔하고 간편하게 개발 가능.
- 별다른 말이 없으면 MVC Model 2 아키텍처를 사용하는 것으로 생각한다.
기본 동작 흐름
전체 흐름은 다음과 같다.
요청 -> 프론트 컨트롤러 -> 핸들러 매핑 -> 핸들러 어댑터 -> 컨트롤러 -> 로직 수행(서비스) -> 컨트롤러 -> 뷰 리졸버 -> 응답(jsp, html)
- 컨트롤러 중에서도, 맨 앞단에서 유저의 유청을 받는 컨트롤러를 프론트 컨트롤러라고 한다.
DispatcherServlet
객체가 이 역할을 한다.- 본격적으로 로직에 들어오기 전에, 요청에 대한 선처리 작업을 수행한다.
- ex. 지역 정보 결정, 멀티파트 파일 업로드 처리 등
- 프론트 컨트롤러는 요청을 핸들러 매핑을 통해 해당 요청을 어떤 핸들러가 처리해야하는지를 매핑한다.
HandlerMapping
객체가 핸들러 매핑에 대한 정보를 담고있다.
- 이렇게 매핑된 핸들러를 실제로 실행하는 역할은 핸들러 어댑터가 담당한다.
HandlerAdapter
객체가 이 역할을 한다.
- 컨트롤러는 해당 요청을 처리하는 로직을 담고있다.
- 보통 요청의 종류 혹은 로직의 분류에 따라 내부적으로 Service 단위로 나누어 모듈화 한다.
- 각 서비스에서는 DB 접근할 수 있는 Repository 객체를 이용하여 데이터에 접근할 수 있다.
- 컨트롤러는 서비스에서의 로직 처리 후, 결과를 뷰 리졸버를 거쳐 뷰 파일을 렌더링하여 내보낸다.
ViewResolver
객체가 이 역할을 한다.
2. Spring MVC 구현
1) DispatcherServlet 을 프론트 컨트롤러로 세팅
<!-- web.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<display-name>Spring JavaConfig Sample</display-name>
<servlet>
<!-- 2. 해당 서블릿의 구현체는 DispatcherServlet 로 정의-->
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 3. contextClass는 AnnotationConfigWebApplicationContext 를 사용-->
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<!-- 4. context 에 대해 따로 설정해둔 클래스의 위치를 파라미터로 줌.
여기서는 사용자가 정의한 WebMvcContextConfiguration 을 사용-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>kr.or.connect.mvcexam.config.WebMvcContextConfiguration</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!-- 1. / 로 들어오는 요청은 mvc 라는 이름의 servlet 이 처리-->
<servlet-name>mvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
// WebMvcContextConfiguration.java
package org.example.guestbook.config;
...
// 설정 파일임을 Spring이 알게함.
@Configuration
// Web에 필요한 빈들을 대부분 자동으로 설정. 주로 아래처럼 커스텀으로 설정해야할 때, WebMvcConfigurerAdapter 를 상속받아 클래스로 구현
@EnableWebMvc
// 해당 패키지에 정의된 클래스중 컴포넌트들을 빈으로 등록해놓음.
// @Controller, @Service, @Repository, @Component 가 달린 객체를 찾음.
@ComponentScan(basePackages = { "kr.or.connect.mvcexam.controller" })
public class WebMvcContextConfiguration extends WebMvcConfigurerAdapter {
// 자바 파일이 아닌, Resource 파일들에 대한 url 요청이 왔을 경우, 해당 경로에서 찾을 수 있게 설정
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/assets/**").addResourceLocations("classpath:/META-INF/resources/webjars/").setCachePeriod(31556926);
registry.addResourceHandler("/css/**").addResourceLocations("/css/").setCachePeriod(31556926);
registry.addResourceHandler("/img/**").addResourceLocations("/img/").setCachePeriod(31556926);
registry.addResourceHandler("/js/**").addResourceLocations("/js/").setCachePeriod(31556926);
}
// default servlet handler를 사용하게 함
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
// / 로 접근시 main 템플릿(jsp) 로 가게함.
@Override
public void addViewControllers(final ViewControllerRegistry registry) {
System.out.println("addViewControllers가 호출됩니다. ");
registry.addViewController("/").setViewName("main");
}
// 렌더링되는 view 파일들의 경로와 확장자명 설정
@Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
2) 이 외 전반적인 모듈 구성
config/*
- 각종 설정 클래스 파일들을 담고 있음.
- 클래스 앞에
@Configuration
이 붙음. - WebMvc 설정 관련 Config 를 제외하고 나머지는 모듈화한 뒤 Application 에서 모두 import
@Controller
관련 빈들은WebMvcContextConfiguration
에서@ComponentScan
으로 찾아줘야하고,@Service
,@Service
,@Component
관련 빈들은ApplicationConfig
에서 찾아줘야 함.- 예를 들어
ApplicationConfig
는 다음과 같음.
@Configuration
@ComponentScan(basePackages = {"org.example.guestbook.dao", "org.example.guestbook.service"})
@Import({DBConfig.class})
public class ApplicationConfig {
}
controller/*
- 각종 컨트롤러 클래스 파일들을 담고 있음.
- 클래스 앞에
@Controller
가 붙음. - 각 컨트롤러 코드는 URI 매핑을 담당.
- Serivce 인스턴스를 가져와 로직을 실행하고, View 단에 나가기 전후 작업을 담당
- 예를 들어
GuestbookController
는 다음과 같음.
@Controller
public class GuestbookController {
// @Autowired 를 통해 스프링이 관리하는 빈을 가져올 수 있음.
@Autowired
GuestbookService guestbookService;
@GetMapping(path="/list")
public String list(@RequestParam(name="start", required = false, defaultValue = "0") int start, ModelMap model) {
// start로 시작하는 방명록 목록 구하기
List<Guestbook> list = guestbookService.getGuestbooks(start);
...
model.addAttribute("list", list);
// list.jsp 로 렌더링
return "list";
}
@PostMapping(path="/write")
public String write(@ModelAttribute Guestbook guestbook,
HttpServletRequest request) {
String clientIp = request.getRemoteAddr();
guestbookService.addGuestbook(guestbook, clientIp);
return "redirect:list";
}
}
dao/*
- DB 에 대해 접근할 때 사용하는 클래스 파일들을 담고 있음.
- 클래스 앞에
@Repository
가 붙음. - 실제 dao 클래스와, 사용할 SQL 만을 담고있는 클래스가 따로 모듈화해서 사용함.
- 예를 들어
GuestbookDao
와GuestbookDaoSqls
는 다음과 같이 생음.
// GuestbookDao.java
import static org.example.guestbook.dao.GuestbookDaoSqls.*;
@Repository
public class GuestbookDao {
private NamedParameterJdbcTemplate jdbc;
private SimpleJdbcInsert insertAction;
private RowMapper<Guestbook> rowMapper = BeanPropertyRowMapper.newInstance(Guestbook.class);
public GuestbookDao(DataSource dataSource) {
this.jdbc = new NamedParameterJdbcTemplate(dataSource);
this.insertAction = new SimpleJdbcInsert(dataSource)
.withTableName("guestbook")
.usingGeneratedKeyColumns("id");
}
public List<Guestbook> selectAll(Integer start, Integer limit) {
...
}
public Long insert(Guestbook guestbook) {
...
}
public int deleteById(Long id) {
...
}
...
}
// GuestbookDaoSqls.java
public class GuestbookDaoSqls {
public static final String SELECT_PAGING = "SELECT id, name, content, regdate FROM guestbook ORDER BY id DESC limit :start, :limit";
public static final String DELETE_BY_ID = "DELETE FROM guestbook WHERE id = :id";
public static final String SELECT_COUNT = "SELECT count(*) FROM guestbook";
}
dto/*
- 데이터를 모델링한 클래스 파일들을 담고있음.
- 필드(프로퍼티)와 Getter, Setter 를 가짐.
- 예를 들어
Guestbook
는 다음과 같이 같음.
public class Guestbook {
private Long id;
private String name;
private String content;
private Date regdate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
...
}
service/*
- 서비스 로직을 담는 클래스 파일들을 담고있음.
- 클래스 앞에
@Service
가 붙음. - Interface로 핵심 로직 먼저 정의한 후, 클래스로 구현.
- 필요한 경우,
Dao
를 직접 사용하는 클래스임. - 예를 들어
GuestbookServiceImpl
는 다음과 같음
@Service
public class GuestbookServiceImpl implements GuestbookService {
@Autowired
GuestbookDao guestbookDao;
@Autowired
LogDao logDao;
@Override
@Transactional // 기본 값 read only transaction 적용
public List<Guestbook> getGuestbooks(Integer start) {
return guestbookDao.selectAll(start, GuestbookService.LIMIT);
}
@Override
@Transactional(readOnly = false)
public int deleteGuestbook(Long id, String ip) {
int deleteCount = guestbookDao.deleteById(id);
Log log = new Log();
log.setIp(ip);
log.setMethod("delete");
log.setRegdate(new Date());
logDao.insert(log);
return deleteCount;
}
...
}
webapp/WEB-INF/views/*
- 렌더링 되는 뷰 관련 파일(
.jsp
)들을 담고있음. - 이 파일들은 뷰 리졸버를 거쳐, 최종적으로 렌더링 되기 직전의 파일임.
3. 레이어드(Layered) 아키텍처
여러 모듈들을 일종의 레이어 단위로 나누는 설계 방식을 말한다.
크게 다음 레이어들로 구성한다.
- Presentation Layer
- 화면 조작 또는 사용자 입력을 처리하는 레이어
- Service Layer (Domain)
- 비즈니스와 관련된 도메인 로직을 처리하는 레이어
- 하나의 비즈니스 로직은 하나의 트랜잭션 단위로 동작.
- Repository Layer (Data source)
- 도메인에서 필요로 하는 데이터를 조작하기 위한 레이어
'더 나은 엔지니어가 되기 위해 > 지금은 안쓰는 자바' 카테고리의 다른 글
[부스트코스 웹 프로그래밍] 인터셉터와 아규먼트 리졸버 (0) | 2020.01.24 |
---|---|
[부스트코스 웹 프로그래밍] 쿠키와 세션 (0) | 2020.01.24 |
[부스트코스 웹 프로그래밍] 스프링 JDBC (1) | 2020.01.20 |
[부스트코스 웹 프로그래밍] 스프링과 DI 컨테이너 (0) | 2020.01.19 |
[부스트코스 웹 프로그래밍] 자바 웹 개발 기초 (0) | 2020.01.17 |