기본 콘텐츠로 건너뛰기

[Spring] 웹 개발에 필요한 기본 정보들

이번 시간에는 웹 어플리케이션 개발에 필요한 기본 정보를 알아보려한다.
MVC 패턴과 이 내용들을 잘 다룰줄 안다면 기본적인 웹 동작은 구현할 수 있을 것이다.

웹 애플리케이션의 종류

화면으로 응답하는 웹 애플리케이션
웹 페이지 형태로 클라이언트에 응답한다.

데이터로 응답하는 웹 애플리케이션
사용자 인터페이스와 데이터를 분리해서 취급하는 애플리케이션은 JSON or Xml을 사용해 데이터 형태로 클라인언트에 응답한다.

애플리케이션 설정

서블릿 컨테이너 설정
스프링 MVC를 이용해 웹 애플리케이션을 할때 ContextLoaderListener, DispatcherServlet, CharacterEncodingFilter를 서블릿 컨테이너에 등록해야 한다.

애플리케이션 컨텍스트 설정
MessageSource와 PropertySourcePlaceholderConfigurer의 빈 정의도 필요하다.

프런트 컨트롤러 설정
자바 기반설정은 @EnableWebMvc를 추가하면 된다. XML에선 <mvc:annotation-driven>요소룰 추하가면 된다.

@Controller 구현

컨트롤러에서 구현하는 처리 내용
  • 선언형 처리 : 메서드 시그니처를 참조해 프런트 컨트롤러가 하는 일
  • 프로그래밍형 처리 : 컨트롤러 클래스의 메서드 안에 하는 일
분류
처리내용
선언형
요청매핑
요청 데이터 취득
입력값 검사 수행
프로그래밍형
입력값 검사 결과 확인
비즈니스 로직 호출
이동 대상 확인 및 데이터 연계
이동 대상 지정

컨트롤러 클래스 작성
@Controller
public class WelcomController {
 
}

POJO 클래스에 @Controller를 지정하면 다음 효과를 얻을 수 있다.
  • 컴포넌트 스캔 기능을 사용해 DI 컨테이너에 빈으로 등록할 수 있다.
  • 요청을 처리하는 메서드(핸들러 메서드)가 정의돼 있는 클래스로 인식한다.

DI 컨테이너 등록
@Configuration
@EnableWebMvc
@ComponentScan("com")
public class WebMvcConfig {
}

컨트롤러 클래스는 컴포넌트 스캔 기능을 사용해 DI 컨테이너에 등록하는 것이 일반적이다.

핸들러 메서드 작성
@Controller
public class WelcomController {
  @RequestMapping("/")
  public String home(Model model) {
      model.addAttribute("now", new Date());
      return "home";
  }
}

@RequestMapping을 지정한 메서드가 핸들러 메서드로 인식된다.
핸들러 메서드로 인식되면 프런트 컨트롤러가 @RequestMapping에 지정된 매핑 정보를 읽고 요청에 대응하는 핸들러 메서드를 자동으로 호출한다.

핸들러 메서드의 매개변수
핸들러 메서드는 인수로 여러 객체를 받을 수 있다.

지정 가능한 주요 타입
Model, RedirectAttributes, 자바빈즈, BindingResult, MultipartFile, Principal 등등이 있다.

지정가능한 주요 어노테이션
  • @PathVariable : URL에서 경로 변수 값을 가져오기위한 에너테이션
  • @RequestParam : 요청 파라미터 값을 가져오기 위한 애너테이션
  • @RequestBody : 요청 본문 내용을 가져오기 위한 애너테이션
  • @ModelAttribute : 모델에 저장된 객체를 인수로 받을 수 있다.
  • @Value : ${}와 같은 플레이스 홀더로 대체된 값이나 #{}와 같은 spEL식의 실행 결과를 인수로 받을 수 이따.
  • 이외 기타 등등이 있다.

관례에 따른 묵시적인 인수 값 설정
  • 인수 타입이 String이나 Integer 같은 간단한 타입인 경우 인수의 이름과 일치하는 요청 파라미터에서 값을 가져올 수 있다.
  • 인수의 타입이 자바빈즈인 경우 자비빈즈의 기본 속성명과 일치하는 객체를 Model에서 가져올 수 있다.

핸들러 메서드의 반환 값
  • String : 이동 대상의 뷰 이름을 반환
  • Model : 이동 대상에 전달할 데이터를 반환
  • ModelAndView : 이동 대상의 뷰 이름과 이동 대상에 전달할 데이터 반환
  • void : HttpServletResponse에 직접 응답 데이터를 쓰거나 RequestToViewNameTransalator의 메커니즘을 이용해 뷰 이름을 결정
  • ResponseEntity<> : 응답 헤더와 응답 본문에 직렬화될 객체를 반환
  • HttpHeaders : 응답 헤더만 반환

지정 가능한 주요 애너테이션
  • @ModelAttribute : Model에 저장하는 객체를 반환한다. (반환값의 형이 자바빈즈인 경우 생략 가능)
  • @ResponseBody : 응답 본문에 직렬화하는 객체를 반환.
뷰 컨트롤러 이용
뷰만 호출하는 경우라면 스프링 MVC가 제공하는 뷰 컨트롤러를 이용할 수 있다.
@Configuration
@EnableWebMvc
@ComponentScan("com")
public class WebMvcConfig extends WebMvcConfigurerAdapter {
  @Override
  public void addViewControllers(ViewControllerRegistry registry) {
      registry.addViewController("/").setViewName("home");
  }
}

요청 매핑

스프링 MVC는 @RequestMapping의 속성값을 사용해 요청 매핑 조건을 지정한다. 스프링 4.3 부터 @RequestMapping의 합성 애너테이션(@GetMapping, @PostMapping, @PutMapping, @DeleteMapping)이 추가 됐다.

요청 경로 사용
요청 경로는 반드시 설정해야 하는 필수 정보로 클래스나 메서드에 매핑한다.
@Controller
@RequestMapping("accounts")
public class WelcomController {
  @RequestMapping("me/email")
  public String home(Model model) {
      model.addAttribute("now", new Date());
      return "home";
  }
}

경로 패턴 사용
요청 경로에 정적으로 표현된 구체적인 경로만이 아니라 동적으로 표현된 경로 패턴도 지정할 수 있다. 다음 3가지가 스프링 MVC에서 지원한다.
  • URI 템플릿 형식 : /account/{accounId}
  • URI 템플릿 형식 + 정규표현식 : /account/{accountId:[a-f0-9-]{36}})
  • 엔트 스타일 : /**/accounts/me/email

HTTP 메서드 사용
HTTP 메서드를 매핑 조건에 지정하는 경우 method 속성을 사용한다. 브라우저에 화면으로 응답하는 웹 애플리케이션에선 HTTP 메서드로 GET, POST 두 가지를 사용할 수 있다.

요청 파라미터 사용
param속성을 이용하면 된다. HTML 측에서 다음 중 한가지 방법으로 요청 파라미터를 지정한다.
  • 링크(a태그)의 경우 URL의 쿼리 문자열에 요청 파라미터를 지정
  • 폼 버튼인 경우 name 속성에 요청 파라미터의 이름을 지정한다.

요청 데이터 취득

경로 변수 값 취득(@PathVariable)
URL에서 경로 변수의 값을 취득할 떄 사용한다.
@RequestMapping(path = "account/{accountId}", method = RequestMethod.GET)
public String detail(@PathVariable String accountId) {
  return "form";
}
@PathVariable을 사용해 취득한 경로 변수의 값은 Model 객체에 같은 이름으로 저장된다.

요청파라미터 취득(@RequestParam)
요청 파라미터 값을 취득하는 인수에 @RequestParam을 지정한다.

폼 클래스 구현

폼클래스는 HTML 폼의 입력 필드 구조를 자바빈드로 표현한 클래스다. HTML에 입력 한 값을 폼 객체에 담아 프런트 컨트롤러에 전달한다.

폼 객체의 스코프
요청 스코프
요청에서 객체를 공유하기 위한 스코프. 단순히 Model에 객체를 저장하는 것만으로 요청 스코프의 객체로 취급된다.

플래시 스코프
Post Redirect Get 패턴의 요청 사이(POST와 리다이렉트 후의 GET의 두 요청사이)에서 객체를 공유하기 위한 스코프.  RedirectAttributes에 객체를 추가하면 된다. 객체를 추가할 땐 addFlashAttribute 메서드를 사용하는 것이 중요하다.

세션스코프
같은 세션의 여러 요청에서 객체를 공유하기 위한 스코프. @SessionAttributes에 세션 스코프로 관리할 대상을 지정하면 된다. 폼 데이터를 <input type=’hidden’>을 사용해 여기저기 가지고 다니는 것이 어려운 경우 폼 객체를 세션 스코프로 관리하는 것을 고려하자.

폼 클래스 작성
serialVersionUID 필드를 정의하고, Seriaiizable을 구현해야 한다.

HTML 포맷과 바인딩
폼 객체를 Model에 저장
뷰에서 폼 객체로 접근할 수 있도록 폼 객체를 Model에 저장한다. 폼 객체를 Model에 저장하는 방법은 2가지가 있다.
  • Model API를 직접 호출 : model.addAttribute() 메서드 사용.
  • @ModelAttribute 애너테이션을 메서드에 붙여주는 방법
    • 핸들러 메서드가 호출되기 전에 실행되고 이떄 반환하는 객체가 Model에 저장된다.
@ModelAttribute(value = "default")
public AccountCreateForm setUpForm() {
  return new AccountCreateForm();
}

@RequestMapping("create")
public String form(Model model) {
  return "form";
}

Model에 저장된 폼 객체와 HTML을 연결하려면
<form:form modelAttribute=”accountCreateForm”> … </form:form> 해주면 된다.

간단한 타입과의 바인딩
간단한 타입의 프로퍼티와 바인딩 하는 경우 HTML 폼의 필드명과 폼 클래스의 프로퍼티 명을 일치시키기만 하면 된다.

간단한 타입의 컬렉션과의 바인딩
컬렉션은 체크박스, 셀렉트 박스와 같이 여러 개의 값을 선택할 때 사용할 수 있다. HTML 폼의 필드명과 폼 클래스의 프로퍼티 명을 일치시키가만 하면 된다.

중첩된 자바 빈즈와 바인딩
프로퍼티명을 ‘.’로 연결한 것을 HTML 폼 필드명으로 지정하면 된다.
public class AccountCreateForm implements Serializable {
  private static final long serialVersionUID = 22929393828384849L;
  private AccountForm account;
  private CardForm card;
}

public class AccountForm implements Serializable {
  private static final long serialVersionUID = -238238947239804L;
  private String name;
  private String email;
}

public class CardForm implements Serializable {
  private static final long serialVersionUID = -23498230948111L;
  private String no;
  private Date validMonth;
}

<form:form modelAttribute="accountCreateForm">
  <span>이름</span><form:input path="account.name"/><br>
  <span>번호</span><form:input path="card.no"/><br>
</form:form>


입력값 검사

스프링 MVC는 Bean Validation 기능을 이용해 요청 파라미터 값이 바인딩된 폼 클래스의 입력값 검사를 한다.

입력값 검사 기능의 활성화
스프링 MVC의 기본 동작에선 폼 클래스에 대한 입력값 검사를 하지 않는다. 하고 싶다면 메서드 매개변수에 폼클래스를 정의하고 Validated 또는 Valid를 지정하자. @Validated를 사용하면 Bean Validation의 유효성 검증 그룹 매커니즘을 이용할 수 있다.
@RequestMapping(method = RequestMethod.GET)
public String search(@Validated AccountSearchForm from, BindingResult result, Model model) {
  if(result.hasErrors() {
     return “searchForm”;
  }
  return "form";
}
  • BindingResult에는 요청 데이터의 바인딩 오류와 입력값 검사 오류 정보가 저장된다.

스프링 MVC에서 유효성 검사를 요청 파라미터가 폼 객체로 바인딩될 때 함께 하게 되고, 형 변환 오류가 발생하면 유효성 검사가 실패한 것으로 간주한다.

미입력 처리
StringTrimmerEditor를 활용해 공백을 처리한다.

입력값 검사 규칙 지정
필수 항목 검사
@NotNull을 사용

자릿수 검사
@Size를 사용. 속성엔 min, max가 있다.

문자 유형 검사
@Pattern을 사용한다.
  • regexp : 정규 표현식의 패턴 문자열을 지정하는 속성
  • flags : 옵션을 지정하는 속성.

입력값 검사 규칙 교체
유효성 검증 그룹 매커니즘을 활용하면 적용할 입력값 검사 규칙을 런타임에 동적으로 교체할 수 있다.

화면 이동

이동 대상을 지정하는 방법
이동 대상을 지정할 땐 핸들러의 메서드가 뷰 이름을 반환하도록 만들면 된다. 뷰 이름을 반환하면 스프링 MVC가 ViewResolver를 통해 논리적인 뷰 이름과 연결된 물리적 뷰를 찾는다.

요청 경로로 리다이렉트
뷰 이름에 ‘redirect:경로’를 지정한다.

요청 파라미터 지정
리다이렉트 시 이동 대상에 요청 파라미터를 전달하려면 RedirectAttributes에 파라미터를 지정한다.
@RequestMapping(method = RequestMethod.GET)
public String search(@Validated AccountSearchForm from, BindingResult result, RedirectAttributes redirectAttributes) {
  redirectAttributes.addAttribute("accountId", form.id);
  return "redirect:/acount/create?complete";
}

경로 변수 지정
리다이렉트할 URL을 동적으로 만들어야 할 때, 경로변수에 들어갈 값을 RedirectAttributes에 저장하면 된다.
redirectAttributes.addAttribute(“accountId”, form.id);
return “redirect:/account/{accountId}?createComplete”;

요청 경로 포워드
뷰 이름에 ‘forward:전송할 경로’를 리턴하면 된다.

뷰와의 데이터 연계
뷰 처리에 필요한 데이터는 Model에 저장해 연계한다. 두 가지 방법이 있다.
  • Model API를 직접 호출
  • ModelAttribute 애너테이션이 붙은 메서드를 준비

@ModelAttribute를 사용해 Model을 저장하는 방법
@ModelAttribute가 붙은 메서드를 준비해 두면 핸들러 메서드가 호출되기 전에 먼저 실행되고, 반환된 객체가 Model에 저장된다.

리다이렉트 대상과의 데이터 연계
RedirectAttributes에 저장하면 된다.  자바 객체를 addFlashAttribute() 함수를 이용해 호출하면 여기에 저장된 객체가 플래시 스코프에 관리된다. addAttribute라는 메서드는 리다이렉트 대상에 요청 파라미터와 경로 변수 값을 전달할 때 사용한다.

뷰 선택


 뷰는 Model에ㅔ 저장된 자바 객체를 참조해 클라이언트에게 반환할 데이터를 만드는 컴퓨넌트다. 실제로 사용할 View 클래스는 ViewResolver 인터페이스의 구현 클래스에 의해 결정된다.

댓글

이 블로그의 인기 게시물

[고량주] 라오왕 연태고량주 플러스

나에게 처음 고량주란 이런것이다 라는걸 알려준 녀석이다. 부모님이 중국집을 하다 보니 가끔 초록색병 고량주를 먹었을때  역한 공업용 알콜 맛에 고량주는 나랑 안맞는다 생각했다가 우연히 양고기에 이녀석을 접한 뒤로 고량주의 맛을 알아버렸다... 제품명 : 라오왕 연태고량주플러스 제품유형 : 일반증류주 도수 : 34.2% 가격 : 9000원(홈플러스 익스프레스 기준) 재구매 의사 : 있다 시음평 : 역시 고량주 특유의 향인데, 열대과일 향도나고, 배향, 살짝 달달한 향이 난다.            목넘김은 34.2%에도 불구하고 그리 힘들지 않았다(주당이 된걸수도..)             중국요리나 양꼬치집에서 맛있는 술이 땡긴다면 강력추천한다.

윈도우 트랙패드로 데트스탑화면 전환

코딩 작업용으로 맥북 트랙패드 사용하다 윈도우에서 마우스 없이 트랙패드로만 사용하려니 이것저것 만지다가 새로운 기능을 발견했다. 1. 새로운 데스크 탑을 만드는 2가지 방법   1) 바탕화면에서 'ctrl' + '윈도우' + 'd'      - 세버튼을 동시에 누르면 새로운 데스크톱 화면이 생성된다.      2) 트랙패드에서 손가락 세개를 사용해 위 방향으로 스크롤      - 위와 같은 화면이 나오는데 우측 하단에 '새 데스크톱'을 클릭하면 추가가 된다. 2. 트랙패드로 데스크톱 화면을 이동하는 2가지 방법   1) 'ctrl' + '윈도우' + 방향버튼(<-(좌) , ->(우))     - 위 버튼을 누르게 되면 데스크톱 화면이 전환이 된다.   2) 트랙패드에서 손가락 네개를 이용해 좌우로 이동     - 위와 같이 데스크톱 화면이 전환이 된다.   3) 세손가락으로 화면 전환은 안될까??     - 곰손인 내가 아기자기한 씽크패드13 같은 트랙패드가 작은 것에서 손가락 4개로 화면전환은 불편함이 있다.😥     - 다음과 같은 설정 방법으로 데스크톱 화면을 전환 할 수 있다.       1>'윈도우키' --> '설정'  --> '장치' --> '트랙패드' --> '세손가락제스처'     - 손가락 제스처 콤보박스를  '바탕화면 전환 및 바탕화면 표시' 로 바꿔주면 손가락 세개로 화면 이동이 가능하다. 마우스를 사용하게 되면 집중력이 떨어진다는 속설(??)이 있어서 최근 마우스를 사용하려 하지 않으려고 한다. 윈도우에서도 이런 꿀팁을 발견하게 되어 개발에 한층 더 집중 할 수 있을 것 같다. 

[자바 웹 프로그래밍]2장 문자열 계산기 구현을 통한 테스트와 리펙토링

이번엔 2장에 나와 있는 내용 정리와 느낀점을 정리 해 보겠다. 1. main() 메소드를 활용한 테스트의 문제점.   - 소스코드 구현 후 정상적으로 동작하는지 확인 위해 일반적인 방법은 main()메소드를 활용하는 것이다.   - 실제 서비스를 담당하는 프로덕션 코드와 이 프로덕션 코드가 정상 동작 하는지 확인을 위한 main() 으로 나뉜다.   - 이 방법의 첫번째 문제점은 프로덕션코드와 main() 메서드가 함께 있다는 것이다.   - 프로덕션 코드와 테스트코드(main)을 분리할 수 있다.   - 두 번째 문제는 내가 구현하고 있는 메서드만 집중 할 수 없고, 클래스가 가지고 있는 모든 메서드를 테스트 할 수 밖에 없다.   - 다른 문제는 항상 콘솔로 확인을 할 수 밖에 없다는 것이다.   - 이를 위해 등장한 라이브러리가 JUnit 이다. 내 관심을 가지는 메서드에 대해 테스트 가능하다. 2. JUnit을 활용해 main() 메서드 문제 극복 2.1 한 번에 메서드 하나에만 집중.   - JUnit관련 라이브러리 추가 후  테스트 메서드에 @Test를 붙이면 된다.   - test 관련 코드 작성 후 Run > Run as> JunitTest를 실행해 보자.   - 각각 테스트 메서드를 독립적으로 실행할 수 있기 때문에 현재 내가 구현하고 있는 프로덕션 코드의 메서드에 집중할 수 있다. import org.junit.Test; public class CalculatorTest { @Test public void add() { Calculator cal = new Calculator(); System.out.println(cal.add(1,2)); } } 2.2 결과 값을 눈이 아닌 프로그램을 통해 자동화 import org.junit.Test; import static org.junit.Assert.assertEquals;