newlec Spring
21. 서비스 객체 사용하기
db를 바인딩해서 데이터를 쿼리하고 결과물을 볼 수 있도록 하자.
라이브러리가 필요하다. 또다시 pom.xml에 추가해주자.
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>NoticeService를 어떻게 생성할 것인가? 직접생성하는 것보다 di로 생성하는게 좋을 듯하다.
<bean id="noticeService" class="com.newlecture.web.service.NoticeService"/>을 넣어주면 주문표로 IOC컨테이너에 담아주게 된다.
전역적으로 사용하는 객체 보따리이기 때문에 컨트롤러에서 가져다 쓸 수 있게된다.
이걸 이제 DI하면된다.
<bean id="/notice/list" class="com.newlecture.web.controller.notice.ListController">
<property name ="noticeService" ref="noticeService"/>
</bean>그리고 컨트롤러에 세터를 만들어주자.
private NoticeService noticeSerivce;
public void setNoticeSerivce(NoticeService noticeSerivce) {
this.noticeSerivce = noticeSerivce;
}dispatcher를 읽으면서 noticeSerivce 객체가 만들어지고
컨트롤러에 세팅되서 컨트롤러가 활용하게 되는 것이다.
jsp때 만들었던 public List<NoticeView> getNoticeViewList(); 와 같은
public List<Notice> getList(); 메소드를 불러와서 사용한다.
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = new ModelAndView("notice.list");
List<Notice> list = noticeSerivce.getList(1, "TITLE", "");
return mv;
}
}페이지1 필드 TITLE 쿼리는 모든것을 받아서 뽑아낸다. 반환값이 LIST니까 list로받는다.
모델에 list를 view가 list로 담아준다.
list.jsp로가서 EL을 통해서 값을 꺼내서 사용하자.
<tbody>
<c:forEach items="${list}" var="notice">
<tr>
<td>${notice.id }</td>
<td class="title indent text-align-left"><a href="detail?=${notice.id }">${notice.title }</a></td>
<td>${notice.wirteId }</td>
<td>
${notice.regDate }
</td>
<td>${notice.hit }</td>
</tr>
</c:forEach>
</tbody>22. 서비스 객체 분리하기
서비스객체로 데이터베이스를 연동했다.
스프링을 이용할때는 서비스가 직접 컨트롤러를 사용하지 않는다 분리해야한다.
결합력을 낮춰야한다.
컨트롤러 멤버변수로 service:JDS <- JDBCService(JDS) - db를 직접활용해서 사용했다.
만약 이렇게 사용하다가 DB를 바꾸거나 JDBC를 사용하지 못하는 상황이 왔다고 생각해보면 서비스를 다시 구현해야한다.
service:JDS 를 했엇기 때문에 이것을 고쳐줘야하는데 결합력이 매우 강한 상태가 된것이다.
이것을 직접사용하지 않고 Interface Service를 활용해서
service:S 기능에만 포커스를 맞춰서 사용하면 구현객체가 바뀐다고 해도 문제가 생기지 않는다.
어떤객체를 구현했는지를 상관을 안하게 하는 것이다.
이 꽃는걸 바뀔때마다 xml로 수정하면된다.
일단 구분을 위해 JDBCNoticeService로 바꾸고 service.jdbc패키지를 만들어서 옮겨주자.
다른 db를 사용할 수도 있다는 것을 보여주기 위해 다른 것도 추가해보자.
사실 이것도 다르다. 데이터베이스 구현체가 서비스에 들어가지 않는다. 나중에는 DAO라는 객체로 db와 연결될 수 있도록한다.
구현객체를 사용할 NoticeService 인터페이스를 추가해준다.
public interface NoticeService {
List<Notice> getList(int page, String field, String query) throws ClassNotFoundException, SQLException;
int getCount() throws ClassNotFoundException, SQLException;
int insert(Notice notice) throws SQLException, ClassNotFoundException;
int update(Notice notice) throws SQLException, ClassNotFoundException;
int delete(int id) throws ClassNotFoundException, SQLException;
}원래 사용하던 서비스는 이 서비스의 구현객체가 된것이다.
public class JDBCNoticeService implements NoticeService {}
나중가선 DAO를 활용해서 DB에서 데이터를 가져오는 코드도 빠지게 된다.
인터페이스 활용 이유를 알기 위해서 가짜로 만들어 놓은 것이다.
인터페이스를 넣고 객체를 정의해서 꽃아야된다. xml파일에서 di를 해줘야한다.
<bean id="noticeService" class="com.newlecture.web.service.jdbc.JDBCNoticeService"/>컨트롤러에서는 NoticeService인터페이스를 필드와 setter에 가지고 있는데
다형성에 의해서 NoticeService인터페이스에 구현객체가 꽃아넣어지는 것이다.
23. 연결정보 분리하기
NoticeService를 사용하고 있는 데이터 연결정보를 별도의 클래스로 분리하자.
db에 연결문자열 id pwd가 있는데 서비스를 배포할때 소스코드를 배포하는게 아니라 컴파일된 bin를 배포해야한다.
비밀번호를 주기적으로 바꿔야하는데 바뀌었을때 단순하게 비밀번호를 바꿔야하는데 소스코드없이 단순히 배포하는게 불가능하다.
다시 컴파일해서 배포해야되는데 하나씩 다바꿔서 배포하는것은 문제가 잇을수잇다.
또한 DB 위치가 바뀌엇을때 localhost가아니라 다른곳일때 문제가생긴다.
이것을 xml에 객체화해서 di하면 텍스트 그대로 배포되기때문에 문제가 안생길 수 있다.
물론 문자열 4개로 담아서 할 수 잇긴하지만 4개정보를 하나의 클래스로 담은게 더 편할 것이다.
이 데이터 정보를 담을 수 있는 인터페이스가 있다.
이것이 데이터 소스이다.
private DataSource dataSource;
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}di할수잇도록 세터도 만들어주자.
기존의 커넥션대신 dataSource로 getConnection()해주면된다.
Connection con = dataSource.getConnection();DataSource dataSource가 null값이니 어떻게 꽃아넣을건지만 고민 하면된다.
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/dbpractice"/>
<property name="username" value="chun"/>
<property name="password" value="1234"/>
</bean>di햇던것처럼꼽아넣어준다. 차이점이라면 이 데이터소스에다가 정보를 꽃아넣어줘야한다.
DataSource 클래스가 가지고 있는 setter네임으로 해야한다.
문자열이기때문에 value로 꽃아넣어준다. 만약 비밀번호가 바뀌었다면 여기서 설정만 수정해주면 되는 것이다.
Spring JDBC 리포지터리를 추가해줘야 사용할 수 있다.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.22.RELEASE</version>
</dependency>24. 스프링 설정파일 분리하기
dispatcher-servlet.xml을 바꿔보자
정해진 이름을 사용하고 정해진위치에 둿다.
다른위치 다른 이름으로 두면 인식이 안됫다.
근데 일반적으로는 원하는 위치와 파일명으로 만들 수 있다.
spring폴더를 만들어주고 복사하고 3개로 나누어준다.
1.서블릿과 관련된 servlet-context
2.서비스와 관련된 service-context
3.보안과 관련된 security-context 으로나눈다.
파일명이 정해진것은 아니고 마음대로 작성하면된다.
근데 굳이 나눈 이유는 뭘까?
프로젝트를 한사람이 만드는게 아니라 여러사람이 만든다.
각각역할을 부여해서 만들기때문에 나눠서 만드는 일이 잇다.
xml파일을 하나만 두면 덮어쓰기 하다가 설정을 망가뜨리거나 작업을 기다리는 동기화 작업을 하면 매우 비효율적이다.
따로 설정을 가지고 있는 것이 바람직하다.
혼자 만든다고 하더라도 나눠서 정리하는 것이 좋다.
역할자가 나누는 방식에 따라서 더 많이 나눌 수 있다.
web.xml로가서 설정을 해줘야한다.
설정파일위치과 이름을 정해놓지 않았기 때문에 WEB-INF에 넣어야했던것이다.
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/servlet-context.xml</param-value>
</init-param>
</servlet>위 처럼 하면 하나의 파일밖에 정할 수 없다.
스프링이 제공하는 listner를 사용하면 여러개의 설정파일을 사용할 수 있다.
톰캣이 시작되거나 세션이 끝날때 등에 이벤트를 지정할 수 있게 해준다.
파라미터값을<context-param>으로 가질수있다.
ContextLoaderListener이 정하는 파라미터 설정을 DispatcherServlet이 사용할 수있다.
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/service-context.xml
/WEB-INF/spring/security-context.xml
</param-value>
</context-param>톰캣이 시작할때 파라미터값을 context-param로가지고 각각의 것들이 실행될때 돌아가면서 들어간다.
<servlet>이 언제 실행이되나면 메모리에 언제 올라갈때 사용된다.
url이잇고 그 매핑이 요청이 오면 메모리에 올라간다.
설정을 다루고 있는 것이기때문에 요청이 올때 메모리에 올라가게 되면 느린속도를 보여주게 된다.
그러면 미리 올라가야하지 않을까?라고 생각하게된다.
톰캣이 시작될때 로드하도록 해주자.
만약 이서블릿이 여러개라면 숫자를 넣어서 우선순위를 넣어줘야한다.
<load-on-startup>1</load-on-startup>이녀석이 로드가 되는것을 다른것들과 동기되서 하나씩 로드하는 것을 겪게 할 수잇지만 비동기적으로 로드하는 것을 원하면
<async-supported>true</async-supported>각각 밑에 넣어주면된다.
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>서블릿이 비동기적으로 로드된다.
25. 객체 DI를 Annotaion으로 변경하기
xml설정을 어노테이션으로 하나씩 바꿔나가려고한다.
자바 APP은 XML과 @어노테이션 두가지로 설정한다.
둘다 장단점이있는데 요즘 스프링은 어노테이션으로 가고 있다.
xml이 좀더 경량화된 프로퍼티(외부 텍스트파일) javaConfig 어노테이션으로 바꿔나가고 있다.
가장 먼저servlet-context DI부분을 어노테이션으로 하도록 하자.
listcontroller에 setter에 @Autowired해준다.
그런데 이것은 소스코드를 손댈수 잇기때문에 가능하고 소스코드를 손댈수없다면 xml을 설정해야한다.
네임스페이스에 xmlns:context를 설정해주고
context:annotation-config/을 추가해줘야 어노테이션을 사용할 수 잇다.
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
세터나 필드에 @Autowired 해준다.
메소드에 @Autowired하면 함수를 통해서 무언가를 더 활용할 여지가 있을 경우 사용하면된다.
뭔가 더 코드를 작성할게 없고 그냥 세팅만 하면된다면 필드에 해도된다.
public class ListController implements Controller {
@Autowired
private NoticeService noticeSerivce;
public void setNoticeService(NoticeService noticeSerivce) {
this.noticeSerivce = noticeSerivce;
}
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = new ModelAndView("notice.list");
List<Notice> list = noticeSerivce.getList(1, "TITLE", "");
mv.addObject("list", list);
return mv;
}
}비슷하게 dataSource객체도 DI해줄수잇다.
public class JDBCNoticeService implements NoticeService {
@Autowired
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}26. Annotation으로 서비스 객체 생성하기
객체 생성 자체를 어노테이션으로 하자. 클래스에 @Component을 달아주면된다.
<context:component-scan base-package="com.newlecture.web.service"/>객체를 먼저 찾아보도록한다.
context:annotation-config/은 필요없게 되니 지워준다.
@Component
public class JDBCNoticeService implements NoticeService {}여기서 문제점은 @Component은 관념적으로 사용하는 것이다.
@Controller @Service @Repository가 의미적으로 특화된 느낌을 더 가지고 있다.
역할에 맞게 작성해주는 것이 좋다.
@Service
public class JDBCNoticeService implements NoticeService {}27. Annotation으로 URL 매핑하기
컨트롤러 객체를 어노테이션으로 생성하고 url매핑 한것도 어노테이션으로 할 수잇다.
<context:component-scan base-package="com.newlecture.web.controller"/> xml에 가서 설정해준다.
그냥 com.newlecture.web만 하면되는거아니냐?
그런데 스캔범위가 넓어지면 모든 패키지를 봐야한다.
이 패키지의 모든 것들을 entity등을 IOC컨테이너에 담을 필요가 없다.
@Controller
public class IndexController {}
어노테이션을 붙였으면 그냥 해야할 행위가 있는 것이기 때문에 참조하는 인터페이스가 있어야했는데 이제 필요가 없어서 메소드도 변경해야한다.
컨트롤러는 반드시 url을 매핑하는 것이라 추가적으로 설정을 해줘야한다.
리턴할때 아무설정을 안해도 @RequestMapping("/index")의 url을 기반으로 페이지를 찾게된다.
심지어 메소드 이름은 아무거나 붙여도된다.
@Controller
public class IndexController {
@RequestMapping("/index")
public void aaaa() {
}
}객체를 만들어도 매핑을 하기 위해서는 xml에 다음 태그를 넣어줘야한다.
<mvc:annotation-driven/>이 컨트롤러와 같은 것으로 역할을 하는데 url을 매핑하기위해 이 태그가 필요한 것이다.
view resolver를 전달하지 않아도 url을가지고 리소스를 알아서 찾게 된다.
그런데 tiles resolver를 찾지 못해서 prefix와 surffix를 매핑해둔 resolver를 찾은것이다.
28. HomController 만들기
어노테이션 적용하는 방법이 포커스엿는데 컨트롤러가 컨트롤러 형태가 아니게 되엇다.
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mv = new ModelAndView("root.index");
return mv;
}원래 이녀석이 가지고 있던 기능을 구현하도록 해줘야한다.
컨트롤러가 더이상 url 매핑되는게 아니라는점
view를 심는방법을 알아야한다.
전에는 클래스 하나에 함수가 잇엇고 함수가 결국 url과 매핑됫는데 이제는 그렇지 않다.
이제는 함수가 여러 개 올 수 있다.
url에 매핑되는 것이 여러개가 될 수 잇다.
더 이상 그냥 컨트롤러가 아닌 여러개를 담을 수 있는 더 큰 그릇이 될 수 있게되는 것이다.
그래서 이제 index로만 한정하면안된다.
root에 view단을 담당하는게 여러개 있다고 생각하고 폴더개념으로 바꾸는 것이 중요하다.
컨트롤러 이름을 HomeController으로 변경해주자.
공지사항도 NoticeController를 만들고 함수를 따로 만들어주자.
컨트롤러를 담는 컨테이너가 되는 것이다.
뷰페이지를 보여주는 방식으로 간단하게 문자열을 반환하면된다.
타일즈에 매핑된 것을 리턴하게 하면된다.
return view페이지;
@Controller
public class HomeController {
@RequestMapping("/index")
public String index() {
return "root.index";
}
}컨트롤러를 담는 큰그릇이라는 것을 이해하자.
29. Notice 컨트롤러 정리하기
ListController와 DetailController 두개의 컨트롤러를 하나로 합치고 함수를 어떻게 정리를 해야한다.
구체적으로 어떻게 나눌까 이것은 컨트롤러나 스프링이아니라
물리학개체로 어떻게 만들건지 파일구조를 어떻게 만들건지 이다.
파일목록을 기준으로 패키지를 만들면되는 것이다.
notice의 목록을 보여주기 자세한내용보여주기 등 기능으로 묶는 것이 좋다.
customer는 대상보다 더 큰 대상이니 클래스가 아닌 패키지로 만들어야한다.
com.newlecture.web.controller.customer 패키지를 만들고 밑에 NoticeController를 만들어주자.
ListController와 DetailController가 클래스였는데 이제 함수만 남으면 되게 된것이다.
각 컨트롤러에서 사용했던 필드 등을 가져와서 그대로 사용하면된다.
@Controller
@RequestMapping("/customer/notice/")
public class NoticeController {
@Autowired
private NoticeService noticeService;
@RequestMapping("list")
public String list() throws Exception{
List<Notice> list = noticeService.getList(1, "TITLE", "");
return "notice.list";
}
@RequestMapping("detail")
public String detail() {
return "notice.detail";
}
}ListController와 DetailController클래스들은 이제 더 이상 사용하지 않으니 지워버린다.
컨트롤러가 매우 가벼워졋다.
30. 컨트롤러를 위한 Annotation 개념정리
https://youtu.be/jyLjTKVbwvY
원래는 매핑과 객체생성을 XML에 하나씩 설정해주었다.
MVC가 스캔을 해서 이름과 상관없이 URL에 맞는 함수를 호출 하는 것으로 하게 된다.
인터페이스라는 제약도 없고 클래스당 개별적으로 만들필요도 없어졋다.
<context:component-scan base-package="com.newlecture.web.service"/>
<mvc:annotation-driven/>컨트롤러를 찾아가도록 설정 하고 추가적으로 어노테이션 매핑을 사용하기 위해서 작성했다.
사용자 요쳥이 들어오면 함수를 호출하기 때문에 이름이 별로 중요하지도 않았다.
매핑에 따라 해주는게 구분하기 쉽다.
반환타입은 아무렇게나 해도된다.
void는 그 이름으로 곧장 가게 된다.
Resolver를 없애면 아예 뷰정보가 없어져서 500에러가 발생하게 된다.
프론트 컨트롤러가 하고 있는 것은 재호출이다.
서블릿이기때문에 우리가 만들어놓은 method를 찾아낸다. 없으면 404에러를 뱉는다.
tiles resolver가 찾기위해선 문자열로 리턴 해줘야한다.
2023.04.20
클래스 오타로 인한 오류 너무 많다.
오류 메시지가 정확히 그 부분을 집는 게 아닌경우가 많다.
뒤로돌아가면서 해보자.
XML을 설정할때는 설정 XML에 객체를 만들어두고
di하는 부분에 각각 꽃아넣어 놓는것이 중요하다.
어노테이션이 이런부분을 쉽게 해주는 것이다.
어노테이션의 원리를 알아야 더 잘 이용할 수 있다고 생각된다.
'기초단계 > SPRING' 카테고리의 다른 글
| 2023.04.24 Spring (0) | 2023.04.24 |
|---|---|
| 2023.04.22 Spring (0) | 2023.04.24 |
| 2023.04.18 Spring (0) | 2023.04.18 |
| 2023.04.16 Spring (0) | 2023.04.17 |
| 2023.04.14 Spring (0) | 2023.04.17 |