[코드로 배우는 스프링 웹 프로젝트 개정판] 3장 정리
스프링 MVC를 이용한 웹 프로젝트에서는 페이지 설정, 테스트 환경 설정, 기본적인 등록, 수정, 삭제, 조회, 목록 구현, 목록 화면의 페이징 처리, 검색 처리 및 페이지 이동 등의 단계가 필요하다. 페이징 처리를 위해서는 페이지 번호와 한 페이지당 보여줄 데이터의 수가 필요하며, MyBatis는 동적 태그 기능을 통해 SQL을 파라미터 조건에 맞게 조정할 수 있다. 또한, 검색 기능은 단일 항목 검색과 다중 항목 검색으로 분류할 수 있으며, 화면에서 검색은 페이지 번호 유지, 검색 조건과 키워드 유지, 한글 처리 등을 고려해야 한다.
Mar 18, 2024
🌼스프링 MVC 프로젝트의 기본 구성
💡핵심 포인트
- 일반적으로 웹 프로젝트는 3-tier 방식으로 구성한다.
- Presentation Tier는 화면에 보여주는 기술을 사용하는 영역이다. Servlet/JSP나 스프링 MVC가 담당하는 영역이 된다.
- Business Tier는 순수한 비즈니스 로직을 담고 있는 영역이다. 이 영역의 설계는 실제로 고객의 요구 사항과 정확히 일치해야 한다.
- Persistence Tier는 데이터를 어떤 방식으로 보관하고, 사용하는가에 대한 설계가 들어가는 계층이다. 일반적으로는 데이터베이스를 이용하지만, 경우에 따라서 네트워크 호출이나 원격 호출 등의 기술이 접목된다.
- 스프링 MVC의 경우 Presentation Tier는 스프링 MVC가, Spring Core는 의존성 주입을 이용한 객체 간의 연관구조를, MyBatis는 SQL에 대한 처리를 담당한다.
각 영역의 Naming Convention
- 프로젝트를 진행할 때에는 다음과 같은 네이밍 규칙을 가지고 작성한다.
xxxController
: 스프링 MVC에서 동작하는 Controller 클래스를 설계할 때 사용한다.xxxService, xxxServiceImpl
: 비즈니스 영역을 담당하는 인터페이스는 ‘xxxService’를, 인터페이스를 구현한 클래스는 ‘xxxServiceImpl’라는 이름을 사용한다.xxxDAO, xxxRepository
: DAO나 Repository라는 이름으로 영역을 따로 구성하는 것이 보편적이다. 다만 MyBatis의 Mapper 인터페이스를 활용할 수도 있다.VO, DTO
: 데이터를 담고 있는 객체를 의미한다. VO는 Read Only의 목적으로, DTO는 데이터 수집의 용도가 더 강하다.
- 패키지의 구성은 프로젝트의 크기나 구성원들의 성향으로 결정한다.
- 예를 들어 규모가 작은 프로젝트는 Controller 영역을 별도의 패키지로 설계하고, Service 영역 등을 하나의 작은 패키지로 설계할 수 있다.
- 반면에, 프로젝트의 규모가 커져서 많은 Service 클래스와 Controller들이 혼재할 수 있다면 비즈니스 단위 별로 패키지를 작성하고, 다시 내부에서 Controller 패키지, Service 패키지 등으로 다시 나누는 방식을 이용한다.
예제 프로젝트 구성
pom.xml
에서 스프링의 버전과 Java 버전 등을 수정한다.- 스프링 관련해서 추가적인 라이브러리로는 spring-tx, spring-jdbc, spring-test가 필요하다.
- MyBatis를 사용한다면 HikariCP, MyBatis, mybatis-spring, Log4jdbc 라이브러리도 추가한다.
- 테스트와 Lombok을 위해서 jUnit 버전을 변경하고, Lombok을 추가한다.
- servlet 3.1을 제대로 사용하기 위해서는 서블릿 3.0 버전 이상으로 수정해야 한다.
root-context.xml
에는 mybatis-spring 네임스페이스를 추가하고, HikariCP와 MyBatis의 설정을 추가한다.
🌼영속/비즈니스 계층의 CRUD 구현
💡핵심 포인트
- 영속 계층의 작업은 항상 다음과 같은 순서로 진행한다.
- 테이블의 칼럼 구조를 반영하는 VO(Value Object) 클래스의 생성
- MyBatis의 Mapper 인터페이스의 작성 / XML 처리
- 작성한 Mapper 인터페이스의 테스트
영속 계층의 구현 준비
- VO 클래스를 생성하는 작업은 테이블 설계를 기준으로 작성한다.
- Lombok을 이용해서 생성자와 getter/setter, toString() 등을 만들어내려면 @Data 어노테이션을 적용한다.
- 간단한 SQL이라면 어노테이션을 이용해서 처리하는 것이 무난하지만, SQL이 점점 복잡해지거나 상황에 따라 다른 SQL문이 처리되는 경우에는 그다지 유용하지 못할 수 있다.
- SQL을 작성할 때는 반드시 ;이 없어야 한다.
- XML을 작성하기 위해 폴더를 생성할 경우 한 번에 생성하는 것이 아닌 하나씩 생성해야 한다.
- XML을 작성할 때는 반드시 <mapper>의 namespace 속성값을 Mapper 인터페이스와 동일한 이름을 줘야 한다.
- <select> 태그의 id 속성값은 메서드의 이름과 일치하게 작성해야 한다.
영속 영역의 CRUD 구현
- 웹 프로젝트 구조에서 구현을 가장 먼저 할 수 있는 영역은 영속 영역이다.
- MyBatis는 내부적으로 JDBC의 PreparedStatement를 활용하고, 필요한 파라미터를 처리하는 ‘?’dp eogks clghksdms ‘#{속성}’을 이용해서 처리한다.
- 자동으로 PK값이 정해지는 경우에는 다음과 같은 두 가지 방식으로 처리할 수 있다.
- insert만 처리되고 생성된 PK 값을 알 필요가 없는 경우
- insert문이 실행되고 생성된 PK 값을 알아야 하는 경우
@SelectKey
는 주로 PK값을 미리 SQL을 통해서 처리해 두고 특정한 이름으로 결과를 보관하는 방식이다.@SelectKey
를 이용하는 방식은 SQL을 한 번 더 실행하는 부담이 있기는 하지만 자동으로 추가되는 PK값을 확인해야 하는 상황에서는 유용하게 사용된다.
- MyBatis는 Mapper 인터페이스의 리턴 타입에 맞게 select의 결과를 처리하기 때문에 모든 파라미터와 리턴 타입의 처리는 get파라미터명(), set칼럼명()의 규칙으로 호출된다.
- 다만 #{속성}이 1개만 존재하는 경우에는 별도의 get파라미터명()을 사용하지 않고 처리된다.
- 특정 데이터의 삭제 역시 PK 값을 이용해서 처린한다.
- Update의 경우 delete와 마찬가지로 몇 개의 데이터가 수정되었는가를 처리할 수 있게 int 타입으로 메서드를 설계할 수 있다.
🌼비즈니스 계층
💡핵심 포인트
- 비즈니스 계층은 고객의 요구사항을 반영하는 계층으로 프레젠테이션 계층과 영속 계층의 중간 다리 역할을 한다.
- 일반적으로 비즈니스 영역에 있는 객체들은 Service라는 용어를 많이 사용한다.
비즈니스 계층의 설정
- 설계를 할 때 각 계층 간의 연결은 인터페이스를 이용해서 느슨한 연결을 한다.
- 메서드를 설계할 때 이름은 현실적인 로직의 이름을 붙이는 것이 관례이다.
@Service
어노테이션은 계층 구조상 주로 비즈니스 영역을 담당하는 객체임을 표시하기 위해 사용한다. 작성된 어노테이션은 패키지를 읽어 들이는 동안 처리된다.- 인터페이스가 정상적으로 동작하기 위해서는 객체가 필요하다. 이는
@Autowired
와 같이 직접 설정해 줄 수 있고, Setter를 이용해서 처리할 수도 있다. @AllArgsConstructor
는 모든 파라미터를 이용하는 생성자를 만들기 때문에 필요한 파라미터를 자동으로 주입할 수 있다.root-context.xml
에서 네이스페이스를 추가하면 해당 이름으로 시작하는 태그들을 활용할 수 있다.
🌼프레젠테이션(웹) 계층의 CRUD 구현
💡핵심 포인트
Controller의 작성
- 스프링 MVC의 Controller는 하나의 클래스 내에서 여러 메서드를 작성하고,
@RequestMapping
등을 이용해서 URL을 분기하는 구조로 작성할 수 있기 때문에 하나의 클래스에서 필요한 만큼 메서드의 분기를 이용하는 구조로 작성한다.`
BoardController의 작성
- 컨트롤러는
@Controller
어노테이션을 추가해서 스프링의 빈으로 인식할 수 있게 하고,@RequestMapping
을 통해서 해당 주소로 시작하는 모든 처리를 지정할 수 있다.
- BoardController는 BoardService에 대해 의존적이므로
@AllArgsConstructor
를 이용해서 생성자를 만들고 자동으로 주입하도록 한다.
- 테스트 클래스의 선언부에는
@WebAppConfiguration
어노테이션을 적용한다. Servlet의 ServletContext를 이용하기 위해서인데, 스프링에서는 WebAppContext 라는 존재를 이용한다. -
@Before
어노테이션이 적용된 메서드는 모든 테스트 전에 매번 실행되는 메서드가 된다.
- MockMvc는 가짜로 URL과 파라미터 등을 브라우저에서 사용하는 것 처럼 만들어서 Controller를 실행해 볼 수 있다.
MockMvcRequestBuilders
라는 존재를 이용해서 GET 방식의 호출을 할 수 있다.MockMvcRequestBuilder
의post()
를 이용하면 POST 방식으로 데이터를 전달 할 수 있고,param()
을 이용해서 전달해야 하는 파라미터들을 지정할 수 있다.
- 리턴 시에는
redirect:
접두어를 사용할 경우 스프링 MVC가 내부적으로response.sendRedirect()
를 처리해 준다.
- 조회의 경우 GET 방식으로 처리하므로
@GetMapping
을 이용한다. @RequestParam
을 이용해서 값을 지정하면 좀 더 명시적으로 처리할 수 있다.
- 수정 작업의 경우 등록과 유사하게 POST 방식으로 동작하므로
@PostMapping
을 이용해서 처리한다.
- 삭제 처리도 반드시 POST 방식으로 처리한다.
🌼화면 처리
💡핵심 포인트
- 화면을 개발하기 전에는 반드시 화면의 전체 레이아웃이나 디자인이 반영된 상태에서 개발해야 한다.
- 웹 디자이너가 없다면 BootStrap을 이용한 무료 디자인들을 찾아볼 수 있다.
목록 페이지 작업과 includes
- 스프링 MVC의 설정에서 화면 설정은
ViewResolver
라는 객체를 통해서 이루어진다. - 이는 ‘/WEB-INF/views’ 폴더를 이용하는데, 해당 경로는 브라우저에서 직접 접근할 수 없는 경로이므로 반드시 Controller를 이용하는 모델 2 방식에서는 기본적으로 사용해야 한다.
- Eclipse 상에서 실행될 때는 ‘/’ 경로로 설정되지 않고, ‘/controller’ 경로를 가지게 되므로 Tomcat의 설정에서 ‘/’로 조정해야 한다.
- CSS와 JS 파일들의 경로를 수정하는 작업은 브라우저의 개발자 도구를 통해서 확인하며 진핸한다. 개발자 도구를 통해서 현재 브라우저의 Network 부분을 확인하고, 페이지를 새로고침하면 잘못된 URL의 정보를 확인할 수 있다.
- JSP를 작성할 때마다 많은 양의 HTML 코드를 이용하는 것을 피하기 위해 JSP의
include
지시자를 활용해서 페이지 제작 시에 필요한 내용만을 작성할 수 있게 header.jsp나 footer.jsp 등을 작업한다.
- JSP 페이지를 작성하다 보면 JavaScript로 브라우저 내에서의 조작이 필요한 경우가 많다. jQuery를 haeder.jsp에 선언해 두면 작성하는 JSP에서 자유롭게 사용할 수 있다.
등록 입력 페이지와 등록 처리
- 새로운 게시글을 등록했을 때 만일 한글 입력에 문제가 있다는 것을 발견했다면, 브라우저에서 한글이 깨져서 전송되는지를 확인하고, 문제가 없다면 스프링 MVC 쪽에서 한글을 처리하는 필터를 등록해야 한다.
- 브라우저에서 전송되는 데이터는 개발자 도구를 이용해서 확인할 수 있다. 개발자 도구에서 ‘Network’탭을 열어둔 상태에서 데이터를 보내면 해당 내용을 볼 수 있다.
- 만약 컨트롤러에 값이 전달될 때 이미 한글이 깨진 상태라면
web.xml
에org.sprinfframework.web.filter.CharacterEncodingFilter
클래스와UTF-8
파라미터를 가진 필터를 추가해서 문제를 해결할 수 있다.
- 최근에는 브라우저에서 경고창을 띄우는 방식보다 모달창을 보여주는 방식을 많이 이용한다.
- 모달창은 활성화된
<div>
를 선택하지 않고서는 다시 원래의 화면을 볼 수 없도록 막기 때문에 메시지를 보여주는데 효과적인 방식이다. - 모달창을 보여주는 작업은 jQuery를 이용해서 처리할 수 있다.
조회 페이지와 이동
- 최근 웹페이지들은 사용자들의 트래픽을 고려해 목록 페이지에서 새 창을 띄워서 조회 페이지로 이동하는 방식을 선호한다.
- 사용자가 뒤로 가기를 선택하는 경우 의도하지 않은 방향으로 웹 페이지가 동작할 수 있다.
- 이는 브라우저에서 뒤로 가기나 앞으로 가기를 하면 서버를 다시 호출하는 게 아니라 과거에 자신이 가진 모든 데이터를 활용하기 때문이다.
- 이 문제를 해결하려면 windows의 history 객체가 가지는 스택 구조를 잘 활용해야 한다.
게시물의 수정/삭제 처리
- 수정과 삭제는 별개의 페이지에서 하는 것이 일반적이다.
- 수정 혹은 삭제 작업은 POST 방식으로 처리된다.
- POST 방식으로 처리하는 부분을 위해서는 <form> 태그로 내용들을 감싸게 한다.
- <input> 태그에 type=’hidden’ 속성을 부여하면 <form> 태그의 내용은 보이지 않고 버튼만 보이게 처리할 수 있다.
- 사용자가 버튼을 클릭하면 해당 id를 가진 <form> 태그를 전송해야 하므로
.attr( attributeName, value ).submit()
을 통해 처리를 해야 한다.
🌼오라클 데이터베이스 페이징 처리
💡핵심 포인트
- 수 많은 데이터를 한 페이지에서 보여주면 처리 성능에 영향을 미친다. 또한 브라우저에서도 역시 데이터의 양이나 처리 속도에 문제를 일으키게 된다.
- 일반적으로 페이징 처리는 크게 번호를 이용하거나
계속 보기
의 형태로 구현된다. 번호를 이용한 페이징 처리는 과거 웹 초기부터 이어오던 방식이고,계속 보기
는 Ajax와 앱이 등장한 이후에무한 스크롤
이나더 보기
와 같은 형태로 구현된다.
order by의 문제
- 데이터의 양이 많을수록 정렬이라는 작업은 많은 리소스를 소모한다.
- 데이터베이스를 이용할 때 웹이나 애플리케이션에서 가장 신경 쓰는 부분은 다음과 같다.
- 빠르게 처리되는 것
- 필요한 양만큼만 데이터를 가져오는 것
- 빠르게 동작하는 SQL을 위해서는 먼저 order by를 이용하는 작업을 가능하면 하지 말아야 한다.
order by 보다는 인덱스
- 가장 일반적인 해결책은 인덱스를 이용해서 정렬을 생략하는 방법이다. 인덱스라는 존재가 이미 정렬된 구조이므로 이를 이용해서 별도의 정렬을 하지 않는 방법이다.
select /*+ INDEX_DESC(tbl_board pk_board) */ * from tbl_board where bno > 0;
인덱스를 사용하는 정렬
- 인덱스에서 가장 중요한 개념 중 하나는
정렬이 되어 있다는 점
이다. 정렬이 이미 되어있는 상태이므로 데이터를 찾아내서 이들을 SORT 하는 과정을 생략할 수 있다.
- 실무에서도 데이터의 양이 많고 정렬이 필요한 상황이라면 우선적으로 생각하는 것이
인덱스
를 작성하는 것이다.
- 오라클은 select 문을 전달할 때 힌트라는 것을 사용할 수 있다. 힌트는 데이터베이스에
지금 내가 전달한 select 문을 이렇게 실행해 줬으면 좋겠다
고 전해주는 것이다. - 힌트 구문에서 에러가 나도 SQL 실행에 지장을 주지 않기 때문에 실행 계획을 통해서 개발자가 원하는 대로 SQL이 실행되는지 확인하는 습관을 가져야 한다.
ROWNUM과 인라인뷰
- 오라클 데이터베이스는 페이지 처리를 위해서 ROWNUM이라는 키워드를 사용해서 데이터에 순번을 붙여 사용한다.
- 모든 SELECT 문에는 ROWNUM이라는 변수를 이용해서 해당 데이터가 몇 번째로 나오는지 알아낼 수 있다.
- 한 페이지당 20개의 데이터를 출력한다고 가정하면 ROWNUM 조건을 WHERE 구문에 추가해서 작성할 수 있다.
select /*+ INDEX_DESC(tbl_board pk_board) */ rownum rn, bno, title, content from tbl_board where rownum <= 20;
- 인라인 뷰는
SELECT문 안쪽 FROM에 다시 SELECT문
을 작성하는 것이다. - 즉 논리적으로는 어떤 결과를 구하는 SELECT 문이 있고, 그 결과를 다시 대상으로 삼아서 SELECT를 하는 것이다.
🌼MyBatis와 스프링에서 페이징 처리
💡핵심 포인트
- 페이징 처리를 위해서 필요한 파라미터는 페이지 번호, 한 페이지당 몇 개의 데이터를 보여줄 것인가를 정하는 두 가지이다.
- 두 파라미터를 별도로 전달하는 방식도 가능하지만 이 데이터들을 하나의 객체로 묶어서 전달하는 방식이 좀 더 확장성이 좋다.
MyBatis 처리와 테스트
- 인터페이스와 어노테이션을 이용할 때 페이징 처리의와 같이 경우에 따라 SQL 구문 처리가 필요한 상황에서는 복잡하게 작성된다.
- XML을 이용할 경우에
< , >
는 태그로 인식되는데, 이를 막기 위해선[CDATA[ ]]
를 사용한다.
🌼페이징 화면 처리
💡핵심 포인트
- 페이지를 보여주는 작업은 다음과 같은 과정을 통해 진행된다.
- 브라우저 주소창에서 페이지 번호를 전달해서 결과를 확인하는 단계
- JSP에서 페이지 번호를 출력하는 단계
- 각 페이지 번호에 클릭 이벤트 처리
- 전체 데이터 개수를 반영해서 페이지 번호 조절
페이징 처리할 때 필요한 정보들
- 화면에 페이징 처리를 하기 위해서는 다음과 같은 정보들이 필요하다.
- 현재 페이지 번호
- 이전과 다음으로 이동 가능한 링크의 표시 여부
- 화면에서 보여지는 페이지의 시작 번호와 끝 번호
- 페이지를 계산할 때 끝 번호를 먼저 계산하는 것이 수월하다.
this.endPage = (int)(Math.ceil(페이지번호 / 10.0)) * 10;
- 만약 화면에 10개씩 글을 보여준다면 시작 번호는 무조건 끝 번호에서 9를 뺸 값이 된다.
this.startPage = this.endPage - 9;
- 끝 번호는 전체 데이터 수에 의해 영향을 받는다. 전체 데이터 수가 80개라고 가정하면 끝 번호는 10이 아닌 8이 되어야 한다.
- 만일 끝 번호와 한 페이지당 출력되는 데이터 수의 곱이 전체 데이터 수보다 크다면 끝 번호는 다시 total을 이용해서 다시 계산되어야 한다.
realEnd = (int) (Math.ceil((total * 1.0) / amount) ); if(realEnd < this.endPage) { this.endPage = realEnd; }
- 이전은 시작 번호가 1보다 큰 경우라면 존재한다.
this.prev = this.startPage > 1;
- 다음으로 가는 링크는 realEnd가 끝 번호보다 큰 경우에만 존재하게 된다.
this.next = this.endPage < realEnd;
🌼검색 처리
💡핵심 포인트
- 검색 조건은 일반적으로
<select>
태그를 이용해서 작성하거나<checkbox>
를 이용하는 경우가 많다.
검색 기능과 SQL
- 게시물의 검색 기능은 다음과 같이 분류가 가능하다.
- 제목/내용/작성자와 같이 단일 항목 검색
select * from ( select /*+index_desc(tbl_board pk_board) */ rownum rn, bno, title, content, writerm regdate, updatedate from tbl_board where title like '%Test%' and rownum <=20 ) where rn > 10;
select * from ( select /*+index_desc(tbl_board pk_board) */ rownum rn, bno, title, content, writerm regdate, updatedate from tbl_board where (title like '%Test%' or content like '%Test%') and rownum <=20 ) where rn > 10;
MyBatis의 동적 SQL
- 검색 조건이 변하면 SQL의 내용 역시 변하기 때문에 고정된 문자열을 작성하는 방식으로는 제대로 처리할 수 없다.
- MyBatis는 동적 태그 기능을 통해서 SQL을 파라미터들의 조건에 맞게 조정할 수 있다.
<if>
는 test 라는 속성과 함께 특정한 조건이 true가 되었을 때 포함된 SQL을 사용하고자 할 때 사용한다.
<if test="type == 'T'.toString()"> (title like '%'||#{keyword}||'%') </if> <if test="type == 'C'.toString()"> (content like '%'||#{keyword}||'%') </if> <if test="type == 'W'.toString()"> (writer like '%'||#{keyword}||'%') </if>
<choose> <when test="type == 'T'.toString()"> (title like '%'||#{keyword}||'%') </when> <when test="type == 'C'.toString()"> (content like '%'||#{keyword}||'%') </when> <when test="type == 'W'.toString()"> (writer like '%'||#{keyword}||'%') </when> <otherwise> (title like '%'||#{keyword}||'%' or content like '%'||#{keyword}||'%' or writer like '%'||#{keyword}||'%') </otherwise> </choose>
<trim>
, <where>
, <set>
은 단독으로 사용되지 않고 <if>, <choose>와 같은 태그들을 내포하여 SQL들을 연결해 주고, 앞 뒤에 필요한 구문들 (and, orm where등)을 추가하거나 생략하는 역할을 한다. <where>
의 경우 태그 안쪽에서 SQL이 생성될 때는 where 구문이 붙고, 그렇지 않은 경우에는 생성되지 않는다.
select * from tbl_board <where> <if test='bno != null"> bno = #{bno} </if> </where>
<trim>
은 하위에서 만들어지는 SQL문을 조사하여 앞 쪽에 추가적인 SQL을 넣을 수 있다.select * from tbl_board <where> <if test='bno != null"> bno = #{bno} </if> <trim prifix = "and" > rownum = 1 </trim> </where>
<foreach>
는 List, 배열, 맵 등을 이용해서 루프를 처리할 수 있다. 주로 in 조건에서 많이 사용하지만, 경우에 따라서 복잡한 where 조건을 만들 때에도 사용한다.Map<String, String> map = new HashMap<>(); map.put("T", "TTTT"); map.put("C", "CCCC"); select * from tbl_board <trim prefix="where (" suffix=")" prefixOverrides="or"> <foreach item="val" index="key" collection="map"> <trim prefix="or"> <if test="key == 'C'.toString()"> content = #{val} </if> <if test="key == 'T'.toString()"> title = #{val} </if> <if test="key == 'W'.toString()"> writer = #{val} </if> </trim> </foreach> </trim>
검색 조건을 위한 Criteria의 변화
- MyBatis는
<sql>
이라는 태그를 이용해서 SQL의 일부를 별도로 보관하고, 필요한 경우에 include시키는 형태로 사용할 수 있다. <sql>
태그는 id라는 속성을 이용해서 필요한 경우에 동일한 SQL의 일부를 재사용할 수 있게 한다.
화면에서 검색 조건 처리
- 화면에서 검색은 다음과 같은 사항들의 주의하면서 개발해야 한다.
- 페이지 번호가 파라미터로 유지되었던 것처럼 검색 조건과 키워드 역시 항상 화며 이동 시 같이 전송되어야 한다.
- 화면에서 검색 버튼을 클릭하면 새로 검색을 한다는 의미이므로 1페이지로 이동한다.
- 한글의 경우 GET 방식으로 이동하는 경우 문제가 생길 수 있다.
- 웹페이지에서
RedirectAttributes
파라미터를 매번 유지하는 일이 번거롭고 힘들다면UriConponentsBuilder
라는 클래스를 이용해서 여러 개의 파라미터들을 연결해서 URL의 형태로 만들 수 있다.
🏁결론
해당 내용을 정리하면서 스프링 MVC를 이용하는 웹 프로젝트 전체 구조에 대한 이해, 개발의 각 단계에 필요한 설정 및 테스트 환경, 기본적인 등록, 수정, 삭제, 조회, 리스트 구현, 목록 화면의 페이징 처리, 검색 처리와 페이지 이동에 대한 개념을 이해할 수 있었다.
Share article