[231004] mvc 복습, controller, 요청 파라미터 처리 복습, model, 리다이렉트, 필터링, DI, Autowired

MJ·2023년 10월 12일

수업 TIL🐣💚

목록 보기
56/68

1교시 04_spring_mvc_ex

복습

mvc 프로젝트 구조

spring mvc 동작 원리

  • 요청하면 컨트롤러가 받고 DispatcherServlet(servlet-context.xml)을 통해 처리되고 다시 컨트롤러로 응답이 돌아오면 곧장 jsp로 가는게 아니라 또 다시 DispatcherServlet을 거쳐 응답 (크게 보면 그런 흐름)
  • ViewResolver: 어떤 jsp로 보낼 것인지
  • 요청에서 핵심은 컨트롤러 검색, 응답에서 핵심은 viewResolver

resources 폴더 이름 바꾼 후 servlet-context

만약 resources 폴더 이름을 assets으로 바꿔본다면

servlet-context의 location도 자동으로 assets으로 바뀜. 다만 매핑 주소는 그대로 -> 주소를 요청할 땐 resources로 요청해야 함. 그러면 폴더는 assets 폴더를 찾아갈거임

main.jsp에서 주소요청해보기

바뀐 폴더 이름인 assets이 아닌 매핑 주소인 resources를 적어줬음

근데 아직 컨트롤러가 없어서 서버 돌리면 오류남. 컨트롤러도 만들기

저 주소를 열어줬을 때 main.jsp 열어줄 수 있도록 컨트롤러로 지정해야 함

MvcController.java

@Controller
public class MvcController {

    @RequestMapping(value="/", method =RequestMethod.GET)
    public String main() {
      return "/WEB-INF/main.jsp";  // 뷰리졸버있으니 main으로 써도된다. 지금 프젝에선 쌤이 지우자 해서 지웠음. 그래서 전부 그대로 씀     
    }
}

contextPath 정보를 요청하면 main.jsp로 이동

이제 실행하면

잘 뜬다.


2교시

다른 페이지로 이동하기

작성페이지로 화면 넘겨서 작성창으로 이동
요청을 jsp에서 만들고 실제로 이동하기 위한 controller 작업할 예정

main.jsp

  <div>
    <a href="${contextPath}/write.do">작성페이지로 가기</a>
  </div>

article 폴더의 write.jsp와 연결되도록 해보자

MvcController

@Controller
public class MvcController {

    @RequestMapping(value="/", method =RequestMethod.GET)
    public String main() {
      return "/WEB-INF/main.jsp";      
    }
  • value 값으로 write.do를 연결해준다(contextPath는 적지 않음)
  • a링크를 이용한 요청은 요청방식(값 전달방식)이 get이다. 따라서 method=Request.GET
  • return으로 write.do 요청시 article/write.jsp와 연결되도록 해줌

articleVo 실습 (요청 파라미터 처리 복습, form)


articleVo 내용은 이렇다. 이걸 활용한 실습. 작성화면 만들기

write.jsp

  <div>
    <h3>기사 작성하기</h3>
    <form action="${contextPath}/register.do" method="post">
      <div>
        <label for="articleNo">기사번호</label>
        <input type="text" id="articleNo" name="articleNo">
      </div>
      <div>
        <label for="title">기사제목</label>
        <input type="text" id="title" name="title">
      </div>
      <div>
        <label for="content">기사내용</label>
        <input type="text" id="content" name="content">
      </div>
      <div>
        <button type="submit">기사작성완료</button>
      </div>
    </form>
  </div>
  • form을 준비한다. action에는 요청하는 주소를 적는다.
  • post방식, register.do로 이동

id,name,value

name은 key, value는 value
1. name: 태그명, form submit시 서버에서 name 명으로 값을 가져올 수 있다. (전달받는 입장에서 값을 꺼낼 수 있는 키, value를 찾기 위한 key)

  • 중복되어 사용이 가능하며, action에 해당하는 페이지에 전달할 수 있는 파라미터로 사용
  • Server단에서 request.getParameter(parameterName) 으로 값을 가져옴
  1. id: 중복 불가능하고 주로 js를 다룰때 사용
  • @RequestMapping에 지정한 Server단 (클래스 or 메소드)의 파라미터로 넘어가지 않기 때문에, Server단에서 접근이 불가능
  1. value: 해당 태그의 값(화면에 표시되는 값)
  • name과 value는 request에 값이 전달될 때 Map과 마찬가지로 Key와 Value 쌍의 형식으로 데이터가 저장

    잘 만들어졋다
    이제 기사번호는 int로 기사제목과 기사내용은 string으로 요청을 해서 받는 실습을 진행해보자.

MvcController


write.jsp에서 건너온 3개의 데이터는 여기서 받아야한다. 3가지 방법이 있었다.

1. HttpServletRequest로 받기

  • request로 받아서 파라미터 빼는 작업
  • articleNo는 정수이므로 Integer.ParseInt 작업이 필요

이제 끝. 그런데 톰캣 실행하면 인코딩 오류 뜬다.


이렇게 적었는데 콘솔창엔 한글을 못 읽어내는 거.. 영어는 된다. 일단 영어로 하는 걸로 하고 넘어감.

2. @RequestParam으로 받기

  • 파라미터의 이름을 @RequestParam의 괄호 안 value에 넣어주고 괄호 밖에는 그 값을 저장할 변수를 적어준다.
  • @RequestParam의 장점: 생략이 가능하기 때문에 @RequestParam(value="articleNo") int articleNo 를 int articleNo로 줄여서 쓸 수 있다.

실행결과 콘솔창에 잘 출력된다

3. 커멘드 객체로 받기



정상실행됨


3교시

  • forward는 기본적으로 뭔가를 가지고 이동하기 위한 방법. 반드시 필요한 것이 '저장'하는 것 -> attribute에 저장한다. request.setAttribute("aa",aa)로 특정 데이터를 저장시키고 이동
  • forward는 request와 response를 둘 다 전달하는데 response는 뭔가를 저장하기 위한 저장소는 아니다.
  • 아무튼 이건 jsp 방식이라 스프링에서는 사용하지 않는다고 함. 스프링은 새로운 방식

2교시 수업에 model 추가

HttpServletRequest에 model 추가

MvcController

  • request는 파라미터 처리용으로 그대로 두고 fowarding할 데이터 전달할 저장소로 모델 추가
  • 이동경로도 같은 jsp가 아니라 다른 jsp로 바꿔줌 (write->result)

result.jsp


이게 코드

이게 출력결과(잘 출력됨)

  • *모델은 포워딩할 데이터, 주로 select할 때 쓴다.

    현주한테 물어봤더니
    현주: 포워딩은 select(조회)-GET, 리다이렉트는 insert(삽입) update(수정) delete(삭제) 등 데이터 내용 바뀔때-POST

  • 목록을 전달할 때는 모델에 list 담고, 개별 항목을 전달할 때는 dto 하나를 담는다

@RequestParam에 model 추가

MvcController

	@RequestMapping(value="/register.do", method=RequestMethod.POST)
    public String register2(@RequestParam(value="articleNo") int articleNo
                          , @RequestParam(value="title") String title
                          , @RequestParam(value="content") String content
                          , Model model) {
      ArticleVo vo = new ArticleVo(articleNo, title, content);
      model.addAttribute("vo",vo);
      return "/WEB-INF/article/result.jsp";
    }

이번에는 객체 형태로 전달했다.

result.jsp

vo.뭐뭐뭐로 불러준다

  <div>
    <div>${vo.articleNo}</div>
    <div>${vo.title}</div>
    <div>${vo.content}</div>
  </div>

실행결과 잘나옴

커맨드 객체 이용한 방식에 model 추가

커맨드 객체를 이용하는 방식에서는 모델 선언 및 모델에 저장하는 코드가 불필요, 모델에

MvcController

@RequestMapping(value="/register.do", method=RequestMethod.POST)
    public String register3(ArticleVo vo) {
      return "/WEB-INF/article/result.jsp";
    }

모델에 저장되는 이름은 ArticleVo vo 중 ArticleVo

  • 이름을 바꾸고 싶을 때는 커맨드 객체를 선언한 앞에 @ModelAttribute를 넣고 원하는 이름을 value로 넣어주면 된다. 예시에서는 atcvo로 바꿈
	//MvcController
  @RequestMapping(value="/register.do", method=RequestMethod.POST)
  public String register4(@ModelAttribute(value="atcvo") ArticleVo vo) {
    return "/WEB-INF/article/result.jsp";
  }
	<!--result.jsp-->
  <div>
    <div>${atcvo.articleNo}</div>
    <div>${atcvo.title}</div>
    <div>${atcvo.content}</div>
  </div>

result.jsp

  <div>
    <div>${articleVo.articleNo}</div>
    <div>${articleVo.title}</div>
    <div>${articleVo.content}</div>
  </div>

실행결과 잘나옴


Redirect

리다이렉트가 필요한 세가지

  • 리다이렉트는 insert, update, delete 할 때 필요하다
    • insert: 삽입 후 목록보기로 리다이렉트(/list.do)
    • update: 수정 후 상세보기로 리다이렉트(/detail.do) 수정목록 확인해야하니까
    • delete: 삭제 후 '목록보기'로 리다리렉트(/list.do) 삭제됐는지 확인 필요
  • 리다리엑트는 jsp로 이동하지 않는다 (foward는 jsp로 이동) -> redirect는 항상 .do로 이동해서 매핑

MyController05 만든다 (작성은 안하고 일단 넘어감)

이 뒤로 갑자기 필터얘기시작함. 결론 걍 xml 복붙하면 됨. 리다이렉트 보려면 4교시로 바로 넘어가기

index.jsp에서 MyController05로 요청

  <%-- MyController05으로 요청 --%>
  <div>
    <form action="${contextPath}/faq/add.do" method="post">
      <div>
        <label for="title">제목</label>
        <input type="text" id="title" name="title">
      </div>
      <div>
        <button type="submit">작성완료</button>
      </div>
    </form>
  </div>

인코딩 공통작업하기

만약 이대로 값을 전달하면 한글이 깨질거임
jsp에서는 컨트롤러에 가서 인코딩했었음

request.setCharacterEncoding("UTF-8");

스프링에서도 파라미터로 HttpServletRequest request 받아서 저렇게 처리해주면 불가능한 건 아니지만 이럴 경우 메소드마다 각자 인코딩을 해줘야해서 완전 불편해짐 예를들면 이렇게

적어줘도 add 메소드에만 인코딩이 적용됐을 뿐인거임.

그렇기때문에 공통작업으로 인코딩을 해결해줘야한다.

1. 필터링

  • jsp에서도 사용했던 방법, 컨트롤러 돌기 전에 필터를 먼저 도니까 인코딩을 필터에 맞김

    필터에서 request를 사용할 수 있도록 파라미터에 적혀있음. 그 파라미터를 우리가 아는 request로 바꿔준다 (HttpServlerRequest req = (HttpServletRequest) request;

다음시간에는 필터를 만드는 두가지 방법


4교시

필터링

3교시에 대충 설명했음. 그냥한다. 해보는데 의의를 두라
(어차피 안외워도 되니까 대충 설명해야지)

안되는 예시 (jsp 방식)


필터이름: AccessFilter

Filter mapping 즉 필터가 언제 동작할지에 대해서
/필터이름 -> / (or /* ,/**)로 변경 : contextPath 포함되는 모든 경로에서 매핑돼서 동작하도록

doFilter : 실제로 필터를 처리하는 곳
여기서 인코딩 작업을 해주면됨 (위는 인코딩오류날 예정)

확인해볼 겸 MyController05작성

return은 임시로 쓴거라서 아직 비어있음. jsp는 오류날 예정


필터동작에는 문제없는데(필터동작합니다 정상출력됨) 인코딩은 안됐음
AccessFilter는 지우자

되는 예시


(이 ppt는 3장 mvc 14페이지에 있다)

  • 필터를 web.xml에 작성한다 (WEB-INF 폴더 밑에 있음)
  • 크게 보면 2개의 구조, filter와 filter-mapping
    • filter: 인코딩 필터를 만들거다
    • filter-mapping: 언제 동작하는지 명시할거다
  <!-- Encoding Filter -->
  <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>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

이제 이거 복붙해서 쓰면 됨. 인코딩 문제 없음


이렇게 필터네임 맞춰주고 동작 주소로 모든 경로해주면 모든 곳에서 동작.
걍 복붙해서 쓰면댐


리다이렉트하는 방법

  1. return "redirect:이동경로"; (리턴이용,가장 대표적인 스프링이 지원하는 방법)
  2. location.href='이동경로'; (js코드만들기, 귀찮은 방법)

    두번째 방법은 alert창 만들때 많이 쓰여. 스크립트 태그를 굳이 만들어서 이동하려는건 alert...

리다이렉트 이동경로

  1. 반드시 URLMapping 값을 작성한다. (.do)
  2. 이동할 JSP 경로를 작성할 수 없다.

다음 시간에 자세히 설명


5교시

리다이렉트로 값 전달

  • 이번 시간엔 이해만 하고 다음 시간엔 새로운 개념이 나올 예정
    title이 빈 문자열이면 add 실패(DB 처리 시 insert 성공은 1, 실패는 0임)

MvcController05


과연 이렇게 모델로 데이터를 전달할 수 있을까.
-> 못함. 모델은 jsp로 전달하는건데 redirect는 jsp로 가지를 않는다
=> 그렇기 때문에 리다이렉트에서는 모델 전달이 없다.

그럼 리다이렉트에서는 어떻게 데이터를 전달해야하는지.
-> redirect문에 추가

return "redirect:/faq/list.do?addResult=" + addResult;

addResult를 갖고가고 싶다고 했으니까 그걸 redirect문 자체에 추가하면 된다. addresult는 list.do로 이동한다.

list.do (MvcController05)

@RequestMapping(value="/faq/list.do", method=RequestMethod.GET)
  public String list(@RequestParam(value="addResult") String addResult, Model model) {

    model.addAttribute("addResult", addResult);
    
    // ViewResolver prefix :  /WEB-INF/views/
    // ViewResolver suffix :  .jsp
    return "faq/list";  //    /WEB-INF/views/faq/list.jsp
    
  }

여기서 모델 작업해준다. addResult @RequestParam으로 불러와서 String addResult에 저장하고 이걸 모델 이용해서 드디어 list.jsp로 보내는거

이제 list.jsp로 데이터 잘 넘어간다. 제목 써주는지 안 써주는지에 따라서 0, 1 잘 표시된다. 원래는 ${addResult}해도 아무것도 표시안됐었음

필수 설정 바꿔주기


만약에 주소창에서 ?addRequestResult=1 값을 지워버린다면
그냥 addResult 부분만 사라지는게 아니라 400 오류 난다.

필수 파라미터인 addResult가 보이지 않는다는 거임

이럴때는 @RequestParam의 required를 false로 해주면 해결

public String list(@RequestParam(value="addResult", required=false) 

(addResult 변수 String으로 해준 이유 : int로 해주면 자동으로 paresInt처리가 돼서 int로 저장되는 건데 만약 필수 설정을 없애주고 int로 저장한다면 addResult 들어온 값 자체를 안 받게 되는거라서 parseInt 할 값이 없는데 머하라는거냐 이런 또다른 이유가 발생)

응용

list.jsp에서 script 활용해서 알람으로 뜨게 할 수도 있다


6교시

5교시 예제 그대로 또 한번더.
아까는 faq 목록보기 list로 이동하는걸 보내고 받아서 다시 보내는 방식으로 두번에 나눠서 했는데 사실 리다이렉트도 곧바로 전달할 수 있다.
=> 이 때 사용하는 attribute를 flash attribute라고 부름(스프링에서 새로 생긴 것)
faq 목록보기로 redirect 할 때 addResult를 "flash attribute"로 곧바로 전달하기 --> redirectAttributes 사용

RedirectAttributes

리다이렉트 시 값 전달용으로 만들어짐.

주의할 점.
add attribute를 할 때 그냥 addAttribute도 있고 addFlashAttribute도 있는데 addAttribute 쓰지 않도록 주의 (addAttribute는 모델이랑 똑같음. 5교시꺼보면 알듯이 모델은 리다렉에서 전달 안됨)

  @RequestMapping(value="/faq/add.do", method=RequestMethod.POST)
  public String add2(HttpServletRequest request
                   , RedirectAttributes redirectAttributes) {  // redirect 상황에서 값을 전달할 때 사용한다.
    
    // 요청 파라미터
    String title = request.getParameter("title");
    
    // title이 빈 문자열이면 add 실패
    int addResult = title.isEmpty() ? 0 : 1;
    
    // faq 목록보기로 redirect 할 때 addResult를 "flash attribute"로 곧바로 전달하기
    redirectAttributes.addFlashAttribute("addResult", addResult);
    
    // faq 목록보기로 redirect
    return "redirect:/faq/list.do";
    
  }

이제 아까랑은 달리 이동만 다시 시켜주면 됨

  @RequestMapping(value="/faq/list.do", method=RequestMethod.GET)
  public String list2() {
    return "faq/list";
  }

훨씬 간단해짐

이 밑줄친 과정들이 사라짐. 리다렉 주소에 붙여서 전달하고 그걸 다시 받아서 모델로 다시 전달하는 과정

리다렉 처리 방법 두가지 공부 끝
하나는 주소에 붙여 보내기
하나는 redirectAttributes

뜬금팁: MyController05를 보면 모든 매핑 주소가 다 faq로 시작하는데 이건 프로젝트를 할 때 한 컨트롤러 당 하나의 기능을 구현하기 때문임. MyController05는 faq에 대한 기능을 구현하는 것 (나중에 프젝할 때 이렇게 해야한다)


DI

  • 05_Di 프로젝트 생성, 폼 복사, 웹xml 복사, 홈어쩌구들 삭제, 파일 셋팅 다 생략
  • 이전에 배웠던 것에 대한 설명도 다 생략
    맛보기: @Autowired - getBean 대체

7교시

  • 서비스 만드는 방법: 어쩌구Service는 인터페이스로, 어쩌구ServiceImpl은 어쩌구Service 상속받은 클래스로 생성

(30분 정도 다 생략)

Dependency Injection

Spring Container에 저장된 객체를 가져오는 방식

주요 Annotation

1) @Inject
(1) javax.inject.Inject
(2) 타입(class)이 일치하는 객체를 찾아서 가져온다.
(3) 동일한 타입(class)의 객체가 2개 이상 있다면 이름(id)이 일치하는 객체를 가져온다.
(4) 타입(class)과 이름(id)이 일치하는 Bean이 없는 경우 오류가 발생한다.
(5) 가져올 객체의 이름(id)을 지정하기 위해서 @Named(javax.inject.Named)를 사용할 수 있다.
(6) 필요한 디펜던시

    <dependency>
      <groupId>javax.inject</groupId>
      <artifactId>javax.inject</artifactId>
      <version>1</version>
    </dependency>

2) @Resource
(1) javax.annotation.Resource
(2) 이름(id)이 일치하는 객체를 찾아서 가져온다.
(3) 동일한 이름(id)을 가진 객체가 없으면 오류가 발생한다.
(4) 필요한 디펜던시(Javax Annotation API)

    <dependency>
      <groupId>javax.annotation</groupId>
      <artifactId>javax.annotation-api</artifactId>
      <version>1.3.2</version>
    </dependency>

3) @Autowired
(1) org.springframework.beans.factory.annotation.Autowired
(2) @Inject 기반의 Spring Annotation이다. (타입 기반)

  • 타입(class)이 일치하는 객체를 찾아서 가져온다.
  • 동일한 타입(class)의 객체가 2개 이상 있다면 이름(id)이 일치하는 객체를 가져온다.
  • 타입(class)과 이름(id)이 일치하는 Bean이 없는 경우 오류가 발생한다.

(3) 가져올 객체의 이름(id)을 지정하기 위해서 @Qualifier(org.springframework.beans.factory.annotation.Qualifier)를 사용할 수 있다.

DI 처리 방법

1) 객체를 Spring Container에 넣는다. (아래 3가지 방법 중 하나 이용)
(1) <bean> 태그
(2) @Configuration + @Bean
(3) @Component (제일 간단, 아직 안배움)

  • 클래스 레벨의 Annotation
  • @Component가 추가된 클래스는 자동으로 Spring Container에 객체로 저장된다.
  • Controller의 @Controller와 Service의 @Service와 DAO의 @Repository는 모두 @Component를 가지고 있다.
    (우리가 직접 만든 클래스는 컴포넌트가 편하고 가져와서 써야하는건 1번과 2번으로 쓰는 편)

2) @Autowired를 이용해서 Spring Container에서 원하는 타입의 객체를 가져온다. (아래 3가지 방법 중 하나 이용)
(1) 필드에 주입하기
(2) 생성자에 주입하기
(3) Setter 형식의 메소드에 주입하기


8교시

AutoWired 실습해보기

  • Spring Container에 객체 넣는건 일단 전에 배웠던대로 bean 태그를 이용해서 넣어주고 그 데이터를 autowired로 가져오기
<bean class="com.gdu.app05.service.BoardServiceImpl" id="boardService"/>

1. 필드에 주입하기


new를 대신한게 spring이니까 일단 new 부분 싹 지워줌

// 주입된 boardService 객체의 변경 방지를 위해 final 처리한다.
  @Autowired
  private final BoardService boardService;

@Autowired가 하는 일 = 컨테이너 역할, 타입이 일치하면 bean을 알아서 가져옴 (getBean같은거 할 필요없음)
=> 자동으로 넣어주기 때문에 주입이라고 함

2. 생성자에 주입하기

가져올 bean 태그는 1번과 동일

  // boardService에 final 처리를 하면 생성자 주입만 가능하다.(필드 주입과 Setter 주입은 불가능하다.)
  // 생성자 주입의 @Autowired는 생략할 수 있으므로 @RequiredArgsConstructor와 같은 Annotation으로 대체할 수 있다.

  @Autowired
  public BoardController(BoardService boardService) {
    super();
    this.boardService = boardService;
  }

생성자의 매개변수로 주입이 된다.

Setter 형식의 메소드에 주입하기

	@Autowired
    public void setBoardService(BoardService boardService) { 
      this.boardService = boardService;
    }

실습 쎔 정리본

  @Autowired 사용 예시
   
  ┌-- Spring Container --┐
  │  BoardDto boardDto1  │
  │  BoardDto boardDto2  │
  └----------------------1) 필드에 주입하기
  
    @Autowired
    private BoardDto boardDto1;  // Spring Container에 BoardDto 타입의 객체가 2개가 있으므로 이름이 일치하는 boardDto1 객체를 가져온다.
    @Autowired
    private BoardDto boardDto2;  // Spring Container에 BoardDto 타입의 객체가 2개가 있으므로 이름이 일치하는 boardDto2 객체를 가져온다.
    
    @Autowired
    @Qualifier(value="boardDto1")  // 가져올 객체 이름을 boardDto1으로 지정한다.
    private BoardDto b1;
    
    @Autowired
    @Qualifier(value="boardDto2")  // 가져올 객체 이름을 boardDto1으로 지정한다.
    private BoardDto b2;
  
  2) 생성자에 주입하기
  
    private BoardDto boardDto1;
    private BoardDto boardDto2;
    
    @Autowired  // 스프링 4 이후 생략 가능 (생성자의 autowired)
    public BoardController(BoardDto boardDto1, BoardDto boardDto2) {  // Spring Container에 저장된 객체를 매개변수로 가져온다.
      this.boardDto1 = boardDto1;
      this.boardDto2 = boardDto2;
    }
    
  3) Setter 형식의 메소드에 주입하기
  
    private BoardDto boardDto1;
    private BoardDto boardDto2;
    
    @Autowired
    public void setBean(BoardDto boardDto1, BoardDto boardDto2) {  // Spring Container에 저장된 객체를 매개변수로 가져온다.
      this.boardDto1 = boardDto1;
      this.boardDto2 = boardDto2;
    }

autowire는 매개변수로 자동주입된다. 이름이 꼭 set어쩌구 아니어도 매개변수에 조건 맞는 애 있으면 자동주입


0개의 댓글