JAVA 웹개발 과정 국비 77일차 정리
77일차
※ 배우는 과정이므로 정확하지 않을 수 있습니다.
잘못된 점은 알려주시면 수정하도록 하겠습니다!
즐거웠던 세미 프로젝트가 끝났다.
후기는 내일 정리해서 올리도록 하고.. 오늘부터는 대망의 Spring 진도가 나갔다. 쉽지않았다.
1. Spring Framework
1) Spring Framework
전자정부프레임워크(Spring + 기타 프레임워크들..)가 규정되어있어서 Spring 프레임워크를 기본적으로 알아야만 한다.
스프링의 장점은 1. 편리함 2. 유지보수에 유리 이다.
2) 환경설정
① Workspace 새로 만들기
기존 실습 코드들이 들어있던 workspace와는 다르게 C:\
또는 D:\
와 같이 경로 바로 밑으로 떨어지는 곳에 workspace를 만들어준다.
이유는 경로상에 한글이 있으면 오류가 뜨기 때문에 (치명적인 것은 아니지만) 귀찮으니까 한글이 경로에 없게 만들어주자.
② STS 설치
마켓플레이스 들어가서 스프링을 이클립스에서 쉽게 사용할 수 있게 만들어주는 STS(Spring Tool Suits)
라고 하는 것을 받아준다.
add-on 말고 Spring Tools 3
을 받아야한다. 4는 Boot라고 보면 된다.
모두 체크하고 설치.
③ 새 프로젝트 생성
이클립스를 재시작하고 Open Perspective에서 Spring을 클릭한 뒤, Spring Legacy Project
를 클릭, Spring MVC Project
를 클릭하여 새로운 프로젝트를 생성한다.
간혹 이 레거시 프로젝트 생성이 안보일 때가 있는데, OpenJDK의 위치가 잘못되면 그럴 수 있다고 한다.
추가 요소를 다운받는다는 것을 확인하고 넘어가면, Package명을 어떻게 할건지 우선적으로 나오는데 보통 기업명.프로젝트명.요소의범주로 이루어진 3단구조를 이용한다.
처음 만드는 것은 컨트롤러일 수밖에 없기 때문에 예를 들면 kh.spring.controller라고 적는다.
④ Maven으로 라이브러리 버전 바꿔주기
현재 버전들은 너무 낮은 버전이거나, 이슈가 있는 버전이므로 최신 버전으로 바꿔준다.
pom.xml
로 들어간다- 자바 버전 바꿔주기
<java-version>1.6<java-version>
의 1.6을 11로 바꿔준다.
- 스프링 버전 바꿔주기
org.springframework-version>3.1.1.RELEASE</org.springframework-version>
에서 3.1.1.RELEASE를 5.3.9로 바꿔준다.- 알맞은 버전은
Maven repository
에서 찾아보기.
- log4j 버전 바꾸기
- log4j 이슈 때문에 일단 최신 버전인 2.17.0으로 맞춰 버전을 변경해주었다.
- 다른 조치는 아직까지 없는듯..?
- 설정에서 버전 바꿔주기
- 버전만 다운로드 된 거지 프로젝트에서 사용하겠다는 설정은 별도로 해줘야함
- 프로젝트 우클릭> Properties > Project Facets > 자바 11, Dynamic Web Module 3.1로 바꿔주기.
- ojdbc 추가하기
- DB와 연동시켜야 하므로, ojdbc를
Maven Repository
에서 검색하여 추가해준다. - ojdbc8의 dependency 코드를 추가해주자.
- 가끔 링크가 깨져서 에러가 나는 경우가 있으므로, 잘 찾아서 쓰도록 하자.
- DB와 연동시켜야 하므로, ojdbc를
⑤ 인코딩 설정
Preferences에서 CSS, HTML, JSP, Content Types, Workspace 설정을 모두 UTF-8
로 바꿔준다.
MAC은 패스!
⑥ Server 추가
왼쪽 하단에 No servers are available...
눌러서 Tomcat 추가를 해준다.
우리는 8.5버전을 쓰므로 아파치를 누르고 8.5를 맞춰 클릭
톰캣이 저장되어있는 경로를 설정해준다.
⑦ 포트번호, path 변경
안해줘도 상관은 없지만 편리함을 위해 포트번호를 80번, path=”“로 변경.
3) 조수 두명!
스프링 프로젝트에서는 Maven
과 Spring
이 두 조수가 개발을 지원해준다.
- Maven
- 라이브러리 관리
- 배포 관리
- Spring
- 인스턴스 라이프사이클 관리
- 디자인패턴 관리
- 횡단관심사 관리
- 스케쥴링 관리
- 트랜젝션 관리 등등..
① Maven
기본적으로 프로젝트의 라이브러리를 관리한다.
pom.xml
을 통해 라이브러리를 관리할 수 있다.
위의 2) 환경설정의 ④번이 좋은 예시이다.
② Spring
스프링 MVC 패턴의 순서와 각 개념을 이해해야 스프링이 어떤 일을 하는지 이해하기 쉽다.
ⓐ Spring MVC 구성요소
구성요소 | 하는 일 |
---|---|
Dispatcher Servlet | 진정한 의미의 프론트 컨트롤러. 모든 흐름을 통제한다. |
Handler Mapping | 어떤 클라이언트 요청이 들어오냐에 따라 컨트롤러를 분배 |
ViewResolver | URL을 조립하는 역할 |
Controller | DAO를 불러서 일을 처리 |
ModelAndView | DAO한테 받은 응답을 request에 넣음(Model) + 가야하는 jsp이름(View) |
View | 결과 데이터를 화면에 뿌려준다 |
ⓑ Spring MVC 작동 순서
- 클라이언트가 요청을 전송한다.
- Dispatcher Servlet이 클라이언트의 Request를 분석한다(요청 URI를 확인).
- Dispatcher Servlet : 이 클라이언트의 요청을 처리해줄 수 있는 컨트롤러가 누구지?
- Dispatcher Servlet : Handler Mapping아 누구한테 가야하는지 알려줘~
- Handler Mapping : A컨트롤러로 가면 돼~
- Dispatcher는 Handler Mapping이 알려준 컨트롤러에게 request를 넘긴다.
- A컨트롤러는 받은 request를 DAO에게 넘긴다.
- DAO는 DB와 통신하여 값을 받아온다.
- DAO는 그 응답을 컨트롤러에게 보낸다.
- 컨트롤러는 ModelAndView 객체를 Dispatcher에 돌려보낸다.
- Dispatcher는 ModelAndView를 ViewResolver에게 보냄
- ViewResolver는 URL을 조립해서 다시 Dispatcher에게 보냄.
- Dispatcher는 URL을 받아서 View에게 전달한다.
4) Spring 프로젝트 폴더의 구조
- src/main/java
- DAO, DTO, Controller 등을 실제로 만들어 사용할 폴더
- src/main/resources
- mybatis 프레임워크의 자원이나 다른 자원들을 넣는 폴더.
- HTML, CSS, IMAGE 등을 넣는 폴더가 아니다!
- 자바와 관련된 자원들을 넣을 때 쓴다.
- xml 문서 등을 넣을 때 사용한다.
- src/test/java와 src/test/resources
- jUnit(단위테스트 라이브러리)를 사용할 때 쓴다.
- 우리 과정은 TDD(Test Driven Development)가 아니기 때문에 패스..
- JRE System Library
- 자바가 스프링에 제공해주는 라이브러리
- Maven Dependencies
- 프로젝트에 들어간 라이브러리는 모두 여기에 모인다.
- target
- 배포 전 필요한 것들이 이곳으로 다 복사가 된다. 그 후 target 폴더 안에 있는 내용들이 realPath로 복사가 된다.
- 만들고 실행하는 과정에 자동으로 제어되기 때문에 이 폴더를 만질일은 없다.
- src/main/webapp/resources
- HTML, CSS, JS 등이 모이는 곳.
- src/main/webapp/WEB-INF/views
- jsp 파일들이 모이는 곳
- jsp파일을 클릭하고 실행하는 것이 막힌다.
- WEB-INF 폴더가 외부에서 접근을 차단하기 때문.
- src/main/webapp/WEB-INF/spring
- 폴더 안 servlet-context.xml 과 root-context.xml은 스프링이 읽어들일 설정파일이다.
- src/main/webapp/WEB-INF/classes
- spring 폴더나 views 폴더는 마음대로 지워도 되지만 이 폴더는 지우면 안됨!
- 내가 만든 클래스들이 컴파일되어 들어오는 곳
- 건드리지 않는 것이 좋다.
5) 프로젝트 실행 과정
위의 Spring MVC 작동 순서와는 다른, 프로젝트를 실행했을 때 어떤 순서로 동작하는지를 정리한다.
- Tomcat 실행
- web.xml 분석(톰캣꺼 말고 프로젝트꺼. views 폴더 안에 있는)
- web.xml 분석 중에 Spring Container 생성
- Spring Container 가동되며 servlet-context.xml & root-context.xml 분석
- servlet-context.xml 분석 중에 InternalResourceViewResolver 인스턴스 생성
- Component-scan이 실행되면서 특정 @Annotation이 붙은 클래스들을 인스턴스로 생성
6) localhost만 쳤는데 home.jsp로 어떻게 이동하는가?
HomeController 안에 아래와 같이 home 메소드가 있다고 치자, 여기서 브라우저에 localhost만 입력해도 home.jsp(이미 만들어놓은 기본 jsp)로 이동할 수 있다. 어떻게 이동이 되는 것일까?
@Controller
public class HomeController {
@RequestMapping("/")
public String home() {
return "home";
}
}
- 서버가 켜짐
- 톰캣이 실행됨
- 스프링 컨테이너 생성됨
- InternalResourceViewResolver 인스턴스 생성됨
- 동시에 HomeController 생성됨(component-scan이 돌면서 @controller를 찾아 인스턴스를 생성)
- annotation-driven이 Handler Mapping에게 @controller가 붙은 애들의 존재를 알려준다.(이런 애도 있으니까 같이 조회해~)
- @controller가 붙은 클래스 안에 @RequestMapping 안에 있는 URL을 Handler Mapping에 등록 (/ -> HomeController)
- 클라이언트가 localhost를 주소창에 입력( / 요청)
- Dispatcher가 /를 Handler Mapping한테 물어봄
- 7번에 저장된 정보로 HomeController로 보내짐
- HomeController 안에 home 메소드 실행
① views/web.xml
web.xml
안의 코드를 살펴보자.
ⓐ Dispatcher 인스턴스 생성 코드
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
ⓑ Spring 컨테이너 생성 코드
bean 태그는 스프링 컨테이너에게 인스턴스를 만들라는 명령을 내려준다.
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
ⓒ Spring 컨테이너가 생성되며 아래 두 xml문서를 읽어들인다.
- /WEB-INF/spring/appServlet/servlet-context.xml (Dispatcher 인스턴스 생성 코드 안에 있는)
- /WEB-INF/spring/root-context.xml (pom.xml 안에 있는)
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
② root-context.xml
아직 아무런 내용도 존재하지 않음.
스프링 컨테이너가 생성되며 읽어들여도 아무 일도 발생하지 않는다.
③ servlet-context.xml
ⓐ ViewResolver 인스턴스 생성
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
ⓑ @Annotation이 붙은 클래스들을 인스턴스로 생성
<context:component-scan base-package="kh.spring.controller" />
ⓒ Handler Mapping 에게 알림
@controller라는 어노테이션이 붙은 애들의 존재를 Handler Mapping에게 알려준다.
나중에 얘네들도 조회될 수 있게끔.
즉, @RequestMapping 어노테이션에 있는 url을 Handler Mapping이 알고 있게한다.
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
7) ViewResolver
URL을 조립하는 역할이라고 정리는 했는데.. 어떻게 조립한다는 것일까?
@Controller
public class HomeController {
@RequestMapping("/")
public String home() {
return "home";
}
}
스프링의 @RequestMapping 이 붙은 메소드는 리턴할 수 있는 값의 종류가 3가지이다.
- void
- String
- ModelAndView
여기서 /
요청을 받아서 String값 home을 리턴하게 되면, Dispatcher는 ViewResolver에 이 값을 전달한다.
아래는 servlet-context.xml 안의 ViewResolver 생성 코드이다.
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
prefix는 접두사, suffix는 접미사라는 뜻으로, home 값의 각각 앞 뒤에 이 value 값들을 붙여주면 /WEB-INF/views/home
.jsp 가 된다.
즉, 위의 경로로 갈 수 있게 경로를 조립한다는 뜻이다.
ViewResolver는 위처럼 jsp로만 조립하는 것이 아니라 html, 파일첨부 등 다양하게 쓸 수 있다. 아직은 이해를 돕기 위해 jsp만 두고 실습을 하는 것 같다.
3. MVC2 와 Spring MVC의 차이
기존 세미 프로젝트를 하면서 느꼈던 불편함이 스프링으로 넘어오면서 해소가 되는 것 같다.
아직 스프링을 배운지 1일차이지만, 갈수록 더 느껴질 것 같아 따로 정리를 미리 해본다.
1) JSP로 다이렉트 접근
MVC2에서는 JSP 경로를 주소창에 다이렉트로 적어주면 접근이 가능했다. 대신 컨트롤러를 거치지 않았기 때문에 빈 페이지나 에러 페이지를 볼 수 있었다.
하지만 스프링은 직접적인 주소를 적어서 들어가는 것을 막아놓았기 때문에 이런 상황이 발생되지 않게 한다.
따라서 스프링에서는 redirect는 더이상 쓸 수 없고, forward만 사용이 가능하다.
2) new를 할 수 없다.
MVC2에서는 모든 인스턴스 생성을 개발자가 할 수 있었지만, 스프링에서는 new를 제한하여 더이상 개발자가 new를 할 수 없게 만든다.
대신 스프링 컨테이너가 대신 인스턴스를 생성하여 가지고 있다가 개발자가 필요할 때 꺼내쓰도록 해준다.
스프링에서 인스턴스 생성은 xml 파일에 bean 태그를 통해 이루어진다.(싱글톤으로 만들어진다.)
물론 전부 그런 것은 아니다.
3) 서버에서 값 받기
MVC2에서는 request.getParameter
를 이용하여 name값을 받아왔다.
스프링에서도 메소드에 HttpServletRequest reqeust
를 인자값으로 받아서 getParameter
를 쓸 수 있지만, 그것보다 더 쉬운 방법이 있다.
@RequestMapping("inputProc")
public String inputProc(HttpServletRequest request) {
String name = request.getParameter("name");
String contact = request.getParameter("contact");
return "home";
}
바로 매개변수에 자료형을 일치시키고, name 값을 일치시키면 동일하게 데이터를 받아온다는 것이다.
@RequestMapping("inputProc")
public String inputProc(String name, String contact) {
return "home";
}
또 다른 방법은 DTO를 생성하여 DTO 자체를 받는 것이다.
주의할 점은 name 속성과, DTO의 멤버필드 이름이 동일해야 한다.
seq값은 넘어오지 않기 때문에, 아래 상황에서는 0이 입력되고, String이라면 null이 입력된다.
public class ContactDTO {
private int seq;
private String name;
private String contact;
}
@RequestMapping("inputProc")
public String inputProc(ContactDTO dto) {
System.out.println(dto.getName() +":"+ dto.getContact());
return "home";
}
매개변수는 한개 이상 받을 수 있기 때문에, 여러 값을 한꺼번에 받는 것도 물론 가능하다.
@RequestMapping("inputProc")
public String inputProc(ContactDTO dto, HttpSession session) {
System.out.println(dto.getName() +":"+ dto.getContact());
String id = (String)session.getAttribute("loginId");
return "home";
}
댓글남기기