728x90

RedirectAttributes에서 addAttribute와 addFlashAttribute 차이가 있습니다.

 

1. addAttribute

addAttribute는 보통 해당 페이지로 리다이렉트를 할 때 값을 넘겨주는 용도로 사용한다.

 

addAttribute를 사용하면 URL 뒤에 붙게돼서 addFlashAttribute와는 달리 값이 유지가 됩니다.

위 사진과 같이 URL에 result의 값이 전달됩니다. 

 

2. addFlashAttribute

addFlashAttribute를 사용해서 리다이렉트를 실행하면

addFlashAttribute로 전달한 result는 URL에 존재하지 않는 것을 확인할 수 있다.

 

즉, addAttribute는 URL에 붙어서 값이 전달되어 유지가 되지만 addFlashAttribute는 일회성으로 URL에 붙지 않고 세션 후 재지정 요청이 들어오면 값은 사라지게 됩니다. 즉 addFlashAttribute는 휘발성 성질을 가지고 있습니다. 성공 여부를 가리기 위해 임시적으로 값을 전달할 때 addFlashAttribute를 이용하면 됩니다.

728x90
728x90

HttpServletRequest는 Http Servlet에 대한 요청 정보를 제공하도록 ServletRequest를 확장한 인터페이스이다.

HttpServletRequest는 서블릿 컨테이너가 생성하며 서블릿의 service() 메서드의 매개변수로 보냅니다.

서블릿의 생명주기(Life Cycle)

1. 서블릿 컨테이너가 서블릿 인스턴스의 init() 메서드를 호출하여 초기화한다.

  • 최초 요청시 한 번만 초기화되며 그 이후로는 이 과정을 생략한다.

2. 서블릿이 초기화된 다음부터 클라이언트가 요청을 처리할 수 있다. 각 요청은 별도의 스레드로 처리하고 이때 서블릿의 service() 메서드를 호출한다.

  • 이 안에서 HTTP 요청을 받고 클라이언트로 보낼 HTTP 응답을 만든다.
  • service()는 Http Method에 따라 doGet() 또는 doPost() 등으로 위임하여 처리한다.

3. 서블릿 컨테이너 판단에 따라 서블릿을 메모리에서 내려야 할 시점에 destroy() 를 호출한다.

public class HelloServlet extends HttpServlet {
  @Override
  public void init() throws ServletExcetion {
    System.out.println("init");
  }
  @Override
  public void doGet(HttpServletReqeust req, HttpServletResponse res) throws ServletExcetion {
    System.out.println("doGet");
  }
  @Override
  public void destory() {
    System.out.println("destroy");
  }
}

서블릿(servlet)은 개발자가 HTTP 요청 메시지를 편리하게 사용할 수 있도록 개발자 대신에 HTTP 요청 메시지를 파싱 합니다. 그리고 그 결과를 HttpServletRequest 객체에 담아서 제공합니다. 서블릿 덕분에 개발자들은 HTTP 스펙을 편리하게 사용할 수 있습니다. HttpServletRequest의 핵심 기능은 HttpServlet의 요청을 받아서 꺼내서 쓸 수 있다는 것입니다. 그럼 이제 HttpServletRequest로 어떻게 컨트롤러에서 값을 꺼내는지 알아보겠습니다.

<form id='actionForm' action="/board/user" method='post'>
  <input type="text" name="name" value="lusida" />
  <input type="text" name="age" value="26" />
</form>

위처럼 JSP에서 사람 정보를 입력하고 POST로 넘기면 formData 형식처럼 key와 value의 값으로 HttpServletRequest에 담아서 컨트롤러로 전달합니다.

  • content-type : application/x-www/form-urlencoded
  • 메시지 바디에 쿼리 파라미터 형식으로 전달 username=lusida&age=26

GET의 경우 URL 뒤에 /board/user?name=lusida&age=26 형식으로 전달되는데 물음표(?) 뒤의 문자열들을 쿼리스트링 또는 요청 파라미터라고 부릅니다. GET 방식의 경우에도 ?key=value&key=value 형식으로 HttpServletRequest에 담아서 컨트롤러로 전달합니다.

@PostMapping("/user")
public String user(HttpServletRequest request) {
  String name = request.getParameter("name"); // key 값을 이용해서 꺼내올 수 있다.
  String age = request.getParameter("age"); // key 값은 input 에서 설정한 name 값이다.
  
  // 만약에 JSP 에서 설정한 name="userName" 이라는 키값이 여러개인 경우에는
  // getParameterValues() 메서드를 이용하여 배열로 받아올 수 있다.
  String[] names = request.getParameterValues("name");
  
  return REDIRECT_LIST;
}

중요 : key 값은 input에서 설정한 name 값이다.

 

@RequestParam

HttpServletRequest과 동일하게 @RequestParam 은 1:1 방식입니다. 차이점은 HttpServletRequest 대신 @RequestParam이라는 어노테이션을 사용한다는 점입니다.

<form id='actionForm' action="/board/user" method='post'>
  <input type="text" name="name" value="lusida" />
  <input type="text" name="age" value="26" />
</form>
@PostMapping("/user")
public String user(@RequestParam String name, @RequestParam String age) {
  // @RequestParam 뒤에 붙는 매개변수 변수명은 JSP 에서 설정한 name 의 key 값과 동일해야 한다.
  return REDIRECT_LIST;
}

HttpServletRequest와 @RequestParam을 이용하여 받아오는 경우 요청 파라미터가 많아지면 많아질수록 컨트롤러 내부 코드나 매개변수가 증가하여 코드 가독성이 떨어지고, 작성되는 코드 양이 많아집니다. 이러한 문제를 해결하고자 나온 것이커맨드 객체(Command Object)입니다.

 

커맨드 객체

HttpServletRequest를 통해 들어온 요청 파라미터들을 setter 메서드를 이용하여 객체에 정의되어있는 속성에 바인딩이 되는 객체를 의미한다. 커맨드 객체는 보통 VO 나 DTO를 의미하며, HttpServletRequest로 받아오는 요청 파라미터의 key 값과 동일한 이름의 속성들과 setter 메서드를 가지고 있어야 합니다. 

 

어떻게 자동으로 바인딩을 시켜주냐 하면, 스프링이 내부적으로 HttpServletRequest와 커맨드 객체의 setter 메서드를 이용하여 알아서 바인딩시켜줍니다.

@Getter @Setter
public class User {
  private String name;
  private String age;
}
@PostMapping("/user")
public String user(User user, Model model) {
  String name = user.getName();
  String age = user.getAge();
  
  // user 파라미터를 model 에 담는다.
  model.addAttribute("user", user);
  return REDIRECT_LIST;
}

위에서 user 파라미터를 model에 담는 것을 볼 수 있습니다. 이 코드 또한 @ModelAttribute 어노테이션을 사용하여 제거할 수 있습니다.

 

커맨드 객체의 역할

  1. 컨트롤러에서 View로 바인딩 : View 단에서 form:form 태그를 사용하는 경우
  2. View에서 컨트롤러로 바인딩 : View 단에서 input type="text" 혹은 input type="hidden"으로 값을 컨트롤러로 전송하는 경우
  3. 컨트롤러에서 Mapper.xml로 바인딩 : Mapper.xml 에서 title = #{title}, contents = #{contents}처럼 사용하는 경우, 커맨드 객체를 통해 #{변수명}과 커맨드 객체의 필드명을 통해 바인딩해주는 경우

 

@ModelAttribute 

@ModelAttribute는 크게 메서드명 위에 사용되는 경우와 파라미터 옆에 사용되는 경우로 나뉩니다.

@ModelAttribute는 커맨드 객체와 같이 요청 파라미터들을 객체 프로퍼티에 바인딩시켜준다는 것입니다. 하지만 @ModelAttribute를 생략해도 커맨드 객체를 이용해서 바인딩이 되는데, @RequestParam 또한 생략해도 사실상 바인딩이 가능합니다. 그 이유는 스프링이 내부적으로 String이나 int 등은 @RequestParam으로 보고, 그 외의 복잡한 객체들은 @ModelAttribute 가 생략됐다고 간주하기 때문입니다. 하지만 그렇다고 무조건 생략하는 것은 위험합니다.

@PostMapping("/ins")
public String ins(@ModelAttribute User user, Model model) {
  String name = user.getUserName();
  String age = user.getAge();
  
  // user 객체를 모델에 담는 코드를 작성하지 않아도, 담겨져 있다.
  // 내부적으로 model.addAttribute("user", user); 로 담는다.
  // 만약 객체명과 변수명이 @ModelAttribute UserVo user 로 되어있는 경우에는 어떻게 담길까?
  // 클래스명을 기준으로 카멜케이스를 적용하여 model.addAttribute("userVo", user); 로 담는다.
  
  return REDIRECT_LIST;
}

@ModelAttribute의 역할 중 하나는 model 에 객체를 담아준다는 것입니다. 파라미터 객체 옆에 @ModelAttribute를 사용했을 때 얻는 또 다른 이점은 @ModelAttribute 가 붙은 파라미터를 처리할 때는 @RequestParam과 달리 검증(Validation) 작업을 내부적으로 진행합니다.

@RequestParam의 경우 스프링의 기본 타입 변환 기능을 이용해서 요청 파라미터 값을 메서드 파라미터 타입으로 변환하는데, 만약 숫자 타입의 파라미터라면 문자열 타입으로 들어온 요청 파라미터의 타입 변환을 시도하고 실패하면 Http 400 Bad Request 응답이 클라이언트로 가게 됩니다.

하지만 @ModelAttribute의 경우 내부적으로 검증(Validation) 작업을 진행하기 때문에 setter 메서드를 이용하여 값을 바인딩하려고 시도하다가 예외를 만나지만 작업이 중단되면서 Http 400 Bad Request 가 발생하지는 않습니다. 타입 변환에 실패해도 작업은 계속되며 BindingException 타입의 객체에 담겨서 컨트롤러로 전달됩니다. 보통 등록이나, 수정을 처리하는 핸들러 메서드의 경우 다양한 검증을 실시해야 하고, 사용자의 입력 값에 오류가 있을 때에는 이에 대한 처리를 컨트롤러에게 맡겨야 합니다.

따라서 @ModelAttribute를 통해서 폼의 정보를 전달받는 경우 Errors 객체나 BindingResult 객체를 @ModelAttribute 가 붙은 파라미터 바로 뒤에 선언해서 검증 처리를 실시합니다. Errors 나 BindingResult는 자신의 바로 앞에 있는 파라미터 검증에서 발생한 오류들만 전달해주기 때문에 @Valid 나 @Validated, @ModelAttribute 가 붙은 파라미터 바로 뒤에 선언되어야 합니다.

 

메서드 위에 @ModelAttribute 가 사용되는 경우

컨트롤러에서 메서드 위에 @ModelAttribute 가 사용되는 경우는, 해당 컨트롤러 내의 어떠한 핸들러 메서드들보다 먼저 동작하게 됩니다.

/**
 * @ModelAttribute 메서드가 먼저 동작하기 때문에,
 * 다른 핸들러 메서드에서 model 에 담겨져있는 user 키값을 이용하여 user 객체를 꺼내서 쓸 수 있다.
 */
@ModelAttribute("user")
public String initUser() {
  // 내부적으로 model.addAttribute("user", userService.findUser(FIRST_USER_SEQ)); 형태로 담는다.
  return userService.findUser(FIRST_USER_SEQ); 
}

따라서 여러 핸들러 메서드에서 공통으로 쓰이며, View 단에서도 꺼내 쓸 일이 있는 것들은 이런 식으로 처리해서 사용하기도 합니다.

 

참고 블로그

https://medium.com/webeveloper/modelattribute-%EC%99%80-%EC%BB%A4%EB%A7%A8%EB%93%9C-%EA%B0%9D%EC%B2%B4-command-object-42c14f268324

 

@ModelAttribute 와 커맨드 객체(Command Object)

@ModelAttribute 와 커맨드 객체(Command Object), @RequestParam 과 HttpServletRequest

medium.com

 

728x90
728x90

1 - 3. 스프링 부트 Mapper와 Mapper 오류, Mail 전송

오늘은 회원에 관한 코드들을 스프링에서 스프링 부트로 바꾸는 작업을 진행하였습니다. 코드를 가져와서 실행을 하던 중에 mapper를 찾지 못하는 에러가 발생했습니다. 


1. @Mapper 어노테이션


스프링에서 mybatis를 사용하는 방식은 SqlSession, SqlSessionTemplate을 설정하고 maper네임스페이스. id, parameter 등의 메서드를 통해 쿼리를 사용하였지만 스프링 부트, mybatis 3.0 이상에서는 sqlSessionTemplate을 설정하고, selectone 메서드를 사용하지 않고 @mapper 어노테이션을 이용해 메서드명과 xml 파일의 id를 매핑시켜 편리하게 사용할 수 있다.


mapper 인터페이스에 @Mapper 어노테이션을 붙여주니 해결되었다.

참고 블로그 : https://frozenpond.tistory.com/85

 

[spring] mapper 어노테이션을 통한 springboot, mybatis 세팅하기

spring boot로 프로젝트를 생성, Mybatis 연동하는 예제입니다. 스프링에서 mybatis를 사용하는 방식은 SqlSession, SqlSessionTemplate을 설정하고 selectOne(maper네임스페이스.id, parameter) 등의 메서드를 통..

frozenpond.tistory.com

 


2. 자바 명명규칙

진행하던 중에 프로젝트 패키지 이름이 바뀌고 클래스 이름 등 자바 명명규칙을 어긴 것들을 발견해서 경로들을 수정해주었다. 

참고 블로그 :  https://ozofweird.tistory.com/entry/Java-%EB%AA%85%EB%AA%85-%EA%B7%9C%EC%B9%99


왼쪽은 수정 전 오른쪽은 수정 후 사진이다.

또한 전에 진행했던 데이터 베이스 테이블들 이름이 너무 헷갈려서 명확하게 이름을 다시 지었다.

 


왼쪽은 수정 전 오른쪽은 수정 후 사진이다.


3. Mapper.xml (not found) 오류


유효성 검사를 실시하면 위와 같은 오류가 발생했다. 진짜 찾는데 3일 내내 찾았는데 못 찾았다. 공식문서와 수많은 블로그를 찾아봤는데 다해봤는데도 안됐다. 마지막 4일째 오류를 해결했다... 이유는 Mapper.xml 파일이 하나라도 오류가 나면 전체가 매핑에 실패해 오류가 난다.

mybatis.mapper-locations= mapper/*.xml

application.properties에 위에 코드를 추가시켜준다.


Jsp에서 Mybatis를 이용할 때 스프링 부트에서 만들어주는 resources에 mapper 밑에 Mapper.xml 파일을 추가시켜준다.


보통 Mapper를 찾지 못하는 오류는 mapper namespace의 경로가 잘못되었거나 Mapper.xml 파일의 id와 Mapper.java파일의 메서드 이름이 잘못된 오류이거나 오타이다.


4. 회원가입 인증 메일 전송

회원가입을 할 때 인증번호를 보내는 기능이 있었는데 구글 계정만 있으면 무료로 발송할 수 있는 Gmail SMTP Server를 이용했다. 메일전송은 MailSender 인터페이스를 상속받은 JavaMailSender를 사용해 구현하였습니다. 새로 스프링 부트에는 dependency와 설정이 빠져있어 오류가 발생했다.

implementation 'org.springframework.boot:spring-boot-starter-mail'

build.gradle dependencies에 위에 코드를 작성한다.



Gmail SMTP Server를 이용하려면 위와 사진과 같은 설정이 필요하다.

위의 설정은 application.properties에 추가 해줘야 한다.

spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.username=gmail
spring.mail.password=password

잘되는것을 확인할 수 있다.


오늘의 회고

mapper 오류에 4일을 걸렷다ㅠㅠ 부트로 바꾸는 프로젝트를 진행하기 전에 제가 진행했던 프로젝트여서 코드 분석을 하지 않고 바로 진행했던 점이 문제로 다가왔다. 프로젝트를 끝낸지 7개월이라는 시간이 흘렀고 제가 작성하지 않은 코드들도 있어서 파악이 안되는 문제가 발생했다. 오늘 내일은 코드를 분석하는 작업을 진행하려고 합니다. 분석을 끝내고 위에 진행했던 코드들의 테스트 코드를 블로그에 작성하겠습니다.

728x90
728x90

개요

skyLife_Transformation 프로젝트로 기존에 스프링으로 진행했던 프로젝트에서 성능반복되는 코드고려하지 않고 그저 기능이 구현되고 돌아가기 위한 코드를 작성했던 프로젝트를 스프링 부트로 바꾸고 성능을 개선하는 프로젝트를 진행하려고 합니다. 천천히 단계별로 개선을 시켜나갈 생각입니다.


1-1 스프링에서 스프링 부트

1. 스프링 부트 프로젝트 만들기

https://start.spring.io/ 에서 스프링 부트를 쉽게 만들 수 있습니다.

저는 Build Tool은 Gradle, 스프링 부트 버전은 2.7.0, Packaging은 Jar, Java 11 버전을 사용할 것입니다.

Dependencies는 Lombok, Spring Web, Spring Security, MyBatis Framework, MySQL Driver, Spring REST Docs를 추가해주었습니다.


2.  스프링 부트 JSP를 위한 수정사항

일단 저는 기존에 진행했던 프로젝트에서 뷰는 수정을 하지 않으려고 합니다.

기존 프로젝트는 jsp로 진행을 해서 스프링 부트에서 jsp를 사용해주려면 설정해줘야 하는 것들이 있습니다.

우선 JSP도 템플릿 엔진이기 때문에 사용하기 위해서는 별도의 라이브러리가 필요합니다.

implementation "org.apache.tomcat.embed:tomcat-embed-jasper"

build.gradle를 파일에 dependencies 부분에 위의 코드를 추가를 해줍니다.

또한 JSP를 사용하기 위해서는 webapp와 WEB-INF 폴더를 포함한 구조로 프로젝트를 변경해야 합니다.

스프링 부트에서 사용하는 뷰 리졸버의 경로를 변경해야 합니다. 뷰 리졸버의 경로는 application.properties에서 변경합니다.

spring.mvc.view.prefix=/WEB-INF/view/
spring.mvc.view.suffix=.jsp

위의 코드를 application.properties에 추가시켜줍니다. Controller 코드까지 작성하면 스프링 부트에서 jsp를 사용할 수 있습니다.

프로젝트의 메인화면입니다. 성공적으로 나오는 것을 확인할 수 있습니다.


3. 인텔리제이와 Github연동

인텔리제이 상단 메뉴에서 VCS를 누르고 Enable Version Control Integration을 클릭하면 밑에 사진과 같은 창이 나옵니다.

위의 창에서 ok를 클릭합니다.

위에서 ok를 클릭하면 상단 메뉴가 Git으로 바뀐 것을 확인하실 수 있습니다. Git 메뉴에서 Manage Remotes를 클릭하면 오른쪽 사진과 같은 창이 나옵니다. 오른쪽 사진에서 Add를 눌어주면 URL을 입력할 수 있는 밑에와 같은 창이 나옵니다.

URL 입력 창에 깃허브에서 레파지토리 주소를 복사해서 붙여 넣어줍니다.

밑에 Commit Message를 적을 수 있는 칸이 있고 Commit Message를 작성한 후 Commit and Push를 클릭해 주시면 깃허브에 잘 올라가는 것을 확인하실 수 있습니다.


오늘의 회고

오늘은 스프링 부트에서 프로젝트 메인화면을 띄우는 것부터 진행하였습니다. 뷰를 만들고 필요 없는 코드와 반복되는 코드들을 수정하고 프로젝트 설계부터 효율적인 방법을 고려해 진행할 예정입니다.

728x90

+ Recent posts