스프링 MVC
servlet-api
servlet.jsp-api
jstl-impl
jstl-api
spring-jdbc
spring-data jdbc
mybatis
mybatis-spring
ojdbc11
lombok
spring-test
slf4j-api
logback classic
spring webmvc
tomcat jdbc
jbcrypt
의존성 추가 -> org/choongang/config 패키지 -> webapp/ WEBINF/ templates, web.xml -> org/choongang/member/controller/MemberController.java -> config/MvcConfig -> resource/logback.xml
-> config/DBConfig 추가 -> MvcConfig 에서 DBConfig임포트
DBConfig
-DataSource: 연결 + 커넥션풀 설정
-JdbcTemplate
-PlatformmTransactionManager
-SqlSessionFactory
//build.gradle
plugins {
id 'java'
}
group = 'org.choongang'
version = '1.0-SNAPSHOT'
ext {
springVersion = '6.1.10'
}
repositories {
mavenCentral()
}
dependencies {
implementation "org.springframework:spring-webmvc:$springVersion"
compileOnly 'jakarta.servlet:jakarta.servlet-api:6.0.0'
compileOnly 'jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.1.1'
implementation 'jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api:3.0.0'
implementation 'org.springframework.data:spring-data-jdbc:3.3.1'
implementation 'org.apache.tomcat:tomcat-jdbc:10.1.25'
implementation 'org.mindrot:jbcrypt:0.4'
implementation 'org.glassfish.web:jakarta.servlet.jsp.jstl:3.0.1'
implementation "org.springframework:spring-jdbc:$springVersion"
runtimeOnly 'com.oracle.database.jdbc:ojdbc11:23.4.0.24.05'
implementation 'org.mybatis:mybatis:3.5.16'
implementation 'org.mybatis:mybatis-spring:3.0.3'
compileOnly 'org.projectlombok:lombok:1.18.34'
annotationProcessor 'org.projectlombok:lombok:1.18.34'
implementation 'org.slf4j:slf4j-api:2.0.13'
implementation 'ch.qos.logback:logback-classic:1.5.6'
testImplementation "org.springframework:spring-test:$springVersion"
testImplementation platform('org.junit:junit-bom:5.10.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}
// web.xml
<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
org.choongang.config.MvcConfig
</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
//MvcConfig
@Configuration
@EnableWebMvc
@ComponentScan("org.choongang")
public class MvcConfig implements WebMvcConfigurer {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/templates/", ".jsp");
}
}
//logback.xml
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d %5p %c{2} - %m%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="stdout" />
</root>
<logger name="org.springframework.jdbc" level="TRACE" />
<logger name="mappers" level="DEBUG" />
</configuration>
//MemberController
@Controller
public class MemberController {
@GetMapping("/member/join")
public String join() {
return "member/join";
}
}
//DBConfig
1) 컨트롤러 구현
2) JSP 구현
WebMvcConfigurer 인터페이스 암기
요청(/hello) -> DispatcherServlet -> HandlerMapping -> 컨트롤러 빈(스프링 컨테이너) -> HandlerAdapter -> 컨트롤러 빈 -> 실행 -> ModelAndView
HandlerAdapter : 컨트롤러 빈의 종류가 다양하기 때문에 맞춰서 실행하기 위한 목적
@Controller, Controller 인터페이스의 구현체, HttpRequestHandler 인터페이스 구현체
ModelAndView :
1) Model : 데이터 (EL 속성으로 추가된 데이터)
2) View : 출력 템플릿 경로 정보
: 요청과 응답의 창구 역할을 하는 서블릿 클래스
- 스프링 컨테이너 생성
: 요청 방식 + 주소 -> 스프링 컨테이너에 있는 컨트롤러 빈을 검색
: 형태가 다양한 컨트롤러 빈(@Controller, Controller 인터페이스, HttpRequestHandler 인터페이스) -> 실행 -> ModelAndView로 반환
참고) ModelAndView
- addObject(String name, String value) : EL 속성으로 추가되는 속성
- setViewName(...) : 뷰 경로
요청메서드의 반환값이 String 이미지만 -> HandlerAdpter에서 실행시 ModelAndView 객체로 변환
: ModelAndView 정보 -> 출력을 위한 View 객체 검색
1) 요청 데이터의 이름과 동일한 매개변수를 요청 메서드에 정의하면 자동으로 주입
2) 정의한 변수의 자료형으로 자동 형변환
3) 요청 데이터의 이름과 요청 메서드에 정의한 이름이 다른 경우
@RequestParam("요청 데이터의 이름")
- required : true(기본값) : 요청 파라미터의 필수
요청 데이터
GET : ?이름=값&이름=값
POST : 요청 바디 이름=값&이름=값
HttpServletRequest
String getParameter(String name)
String[] getParameterValues(String name);
커맨드 객체 : 요청쪽 데이터를 자동으로 매핑
//Member
@Data
@Builder
@NoArgsConstructor @AllArgsConstructor
public class Member {
@Id
private Long seq;
private String email;
private String password;
private String userName;
private LocalDateTime regDt;
}
//CrudRepository 상속
package org.choongang.member.repositories;
import org.choongang.member.entities.Member;
import org.springframework.data.repository.CrudRepository;
public interface MemberRepository extends CrudRepository<Member, Long> { }
//test에서
@Autowired
private MemberRepository repository;
// 멤버 조회
@Test
void test1() {
List<Member> members = (List<Member>) repository.findAll();
members.forEach(System.out::println);
}
// 멤버 추가(오류 발생함) (기본키가 있다면 업데이트 쿼리)
@Test
void test2() {
Member member = Member.builder()
.seq(24L)
.email("user06@test.org")
.password("123456789")
.userName("사용자06")
.build();
repository.save(member);
}
// 한 개 조회 및 삭제
@Test
void test3() {
Member member = repository.findById(1L).orElse(null);
System.out.println(member);
repository.delete(member);
}
: 기본키 말고 다른 정보로 가져옴
public interface MemberRepository extends CrudRepository<Member, Long> {
Member findByEmail(String email);
List<Member> findByUserNameContaining(String keyword);
}
@Test
void test4() {
Member member = repository.findByEmail("user02@test.org");
System.out.println(member);
}
너무 길경우 가독성이 떨어짐
// MemberRepository 에서
// 복잡한 쿼리는 직접 명시
@Query("SELECT * FROM MEMBER WHERE USER_NAME LIKE :param1 AND EMAIL LIKE :param2 ORDER BY REG_DT DESC")
List<Member> getMembers(@Param("param1") String key1, @Param("param2") String key2);
// 테스트에서
@Test
void test7() {
List<Member> members = repository.getMembers("%용자%", "%user%");
members.forEach(System.out::println);
}
Pageable 인터페이스
PageRequest 구현 클래스
Page 자료형
public interface MemberRepository extends CrudRepository<Member, Long> {
Page<Member> findByUserNameContaining(String keyword, Pageable pageable);
@Test
void test8() {
Pageable pageable = PageRequest.of(0, 3, Sort.by(desc("regDt"), asc("email")));
Page<Member> data = repository.findByUserNameContaining("용자", pageable);
List<Member> members = data.getContent();
long total = data.getTotalElements(); // 조회된 전체 레코드 갯수
int pages = data. getTotalPages();
members.forEach(System.out::println);
System.out.printf("총 갯수 : %d, 총 페이지 수 : %d%n", total, pages);
}
// entities
@Data
@Builder
@NoArgsConstructor @AllArgsConstructor
//@Table("MEMBER") //테이블 명과 매핑
public class Member {
@Id // 기본 키를 알려줌
@Column("ID") // 해당 명으로 맵핑 시켜줌
private Long seq;
private String email;
private String password;
private String userName;
private LocalDateTime regDt;
}
addAttribute(키, 값)
addAllAttribute(Map ...)
Attribute속성
public class MemberController {
@GetMapping("/member/join")
public String join(Model model) {
model.addAttribute("message", "안녕하세요.");
return "member/join";
}
/*
@GetMapping("/member/join")
public ModelAndView join() {
ModelAndView mv = new ModelAndView();
mv.addObject("message", "안녕하세요");
mv.setViewName("member/join");
return mv;
}
*/
}
EL식으로 jsp 에서 받을 수 있음.
예) /board/** -> /board 경로의 모든 파일과 하위 경로를 포함한 모든 파일
/board/list.jsp
/board/sub/list.jsp
*
/board/* -> /board 경로의 모든파일
/board/list.jsp (O)
/board/sub/list.jsp (X)
?
: 문자 1개
/m?01 -> /ma01, /mb01, /m101
@RequestMapping
@GetMapping
@PostMapping
@PatchMapping
@DeleteMapping
@DeleteMapping