12월 8일 - 수업 34일차 (SPRING)

수아레스·2022년 12월 11일
post-thumbnail

1. 아래를 프로그래밍 하시오.

http://localhost:8282/board/grade/{kor}/{eng}/{math}

grade.jsp에 평균과 총점이 나오도록 하시오.
단, lombok을 쓰고 command 객체로 처리 할것.

Grade.java

package edu.global.ex.vo;

import lombok.AllArgsConstructor; // 모든 필드 값을 파라미터로 받는 생성자 생성
import lombok.NoArgsConstructor; // 파라미터가 없는 기본 생성자 생성
import lombok.Data; // @Getter, @Setter, @RequiredArgsConstructor, @ToString, 
					// @EqualsAndHashCode을 한꺼번에 설정해줌

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Grade {
	private int kor;
	private int eng;
	private int math;
	
	public int getTotal() {
		return kor + eng + math;
	}
	
	public double getAvg() {
		return getTotal() / 3.0;
	}

}

BoardController.java

@Controller
@RequestMapping("/board")
public class BoardController {

	@GetMapping("/grade/{kor}/{eng}/{math}")
	public String grade(@PathVariable int kor,
						@PathVariable int eng,
						@PathVariable int math, Grade grade) {
		
		return "Grade";
	}
}

@PathVariable: uri를 통해 전달된 값을 파라미터로 받아오는 역할

Grade.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	국어 : ${ kor } <br>
	영어 : ${ eng } <br>
	수학 : ${ math } <br>
	총점 : ${ grade.total }<br>
	평균 : ${ grade.avg }
	
</body>
</html>

ㅈㅎ님의 코드를 뽀린 ㅈㅎ님의 코드를 또 뽀려왔어요
ㅈㅎ 세계관 ㅎㄷㄷ;;

2. FOREIGN KEY와 PRIMARY KEY에 대하여 설명하시오.

테이블에 저장된 각각의 데이터를 유일하게 구분하는 키를 기본키(Primary Key)라 부르며 각 테이블 간에 연결을 만들기 위해서 테이블에서 다른 테이블의 참조되는 기본키 칼럼을 외래키(Foregin Key)라 한다. (다른 테이블의 외래키와 연결되어 테이블간에 연결된다.)

🔑 기본 키 (Primary Key)

  • Data-Table에 있는 유일하게 구분되는 Data-Key를 기본 키(Primary Key)라 부른다.
  • Primary Key는 유일한 값이기 때문에 중복된 값을 가질 수 없다.
  • Primary Key는 공백을 가질 수 없다.

🔑 외래 키 (Foreign Key)

  • 한 Table과 참조되는 다른 Table 간의 연결되는 Primary Key Column
  • Foreign Key는 다른 Primary Key를 참조하는 속성 또는 속성들의 집합을 의미한다.
  • Foreign Key는 참조관계의 기본 키와 같은 속성을 가진다.

3. 아래 @애너테이션에 대하여 설명하시오.

@Controller

Spring에게 해당 Class가 Controller의 역할을 한다고 명시하기 위해 사용하는 Annotation

@Controller                   // 이 Class는 Controller 역할을 합니다
@RequestMapping("/user")      // 이 Class는 /user로 들어오는 요청을 모두 처리합니다.
public class UserController {
    @RequestMapping(method = RequestMethod.GET)
    public String getUser(Model model) {
        //  GET method, /user 요청을 처리
    }
}

@RequestMapping

특정 url로부터 요청을 받으면 어떤 Controller에서 처리할 지 알아야 한다. 이 때, 특정 url을 요청을 수행할 Controller과 매핑하여 지정하는 어노테이션이 @RequestMapping이다.

@RequestMapping(value=”“)와 같은 형태로 작성

value는 연결할 url을 지칭한다. 보통 호스트 주소와 포트 번호를 제외하고, REST API에 따른 url을 설계한다.

  • 들어온 URI의 요청과 Annotation value 값이 일치하면 해당 클래스나 메소드 실행
  • Controller 객체 안의 메서드와 클래스에 적용 가능
  • Class 단위에 사용하면 하위 메소드에 모두 적용됨
  • 메소드에 적용되면 해당 메소드에서 지정한 방식으로 URI를 처리
@RequestMapping({"/hello", "/hello-buddy", "/hello/**"}

위와 같이 다중 요청도 가능하며 셋 중 어느 url을 쳐도 다 연결된다.

RequestMapping의 동작 방식

  1. SpringBoot 애플리케이션이 실행되면 애플리케이션에서 사용할 baen들을 담을 ApplicationContext를 생성하고 초기화한다.

  2. @RequestMapping이 붙은 메서드들이 handler에 등록되는 것은 ApplicationContext가 refresh되는 과정에서 일어난다. refresh과정에서 Spring Application 구동을 위해 많은 Baen들이 생성되고, 그 중 하나가 RequestMappingHandlerMapping이다. 이 Bean은 우리가 @RequestMapping으로 등록한 메서드들을 가지고 있다가 요청이 들어오면 Mapping해주는 역할을 수행한다.

@Controller                   // 이 Class는 Controller 역할을 합니다
@RequestMapping("/user")      // 이 Class는 /user로 들어오는 요청을 모두 처리합니다.
public class UserController {
    @RequestMapping(method = RequestMethod.GET)
    public String getUser(Model model) {
        //  GET method, /user 요청을 처리
    }
    @RequestMapping(method = RequestMethod.POST)
    public String addUser(Model model) {
        //  POST method, /user 요청을 처리
    }
    @RequestMapping(value = "/info", method = RequestMethod.GET)
    public String addUser(Model model) {
        //  GET method, /user/info 요청을 처리
    }
}

GET, POST, PUT, PATCH, DELETE 메서드에 대해서 각각에 맞는 메서드 옵션이 적용된 어노테이션(@GetMapping이나 @PostMapping과 같은)이 존재하여 @RequestMapping 하위에 써서 코드를 좀 더 간결하게 만들 수 있다.

@RequestMapping은 Class와 Method에 붙일 수 있고, @GetMapping, @PostMapping, @PutMapping, @DeleteMapping 등은 Method에만 붙일 수 있다.

출처: https://velog.io/@dyunge_100/Spring-%EC%9A%94%EC%B2%AD-%EB%B0%A9%EC%8B%9DRequestMapping-GetMapping-PostMapping

@GetMapping

GET 요청 방식의 API를 만들때, @RequestMapping(method = RequestMethod.GET ...) 방식도 있지만, @GetMapping을 이용하는 방법도 있다.

@RestController
@RequestMapping("api")
public class GetController {

  ...

  // GET Method 통신
  // 경로는 api/getParam과 매핑됨
  @GetMapping("/getParam")
  public String getParameter() {
    return "Hello Spring";
  }

}

단순히 @GetMapping 을 사용하면 @RequestMapping(method = RequestMethod.GET ...) 과 동일한 효과를 볼 수 있다.

@PathVariable

REST API에서 URI에 변수가 들어가는걸 실무에서 많이 볼 수 있다.
예를 들면, 아래 URI에서 1234와 4062464가 @PathVariable로 처리해줄 수 있는 부분이다.
http://localhost:8080/api/user/⭐1234
https://music.bugs.co.kr/album/⭐4062464

사용법
Controller에서 아래와 같이 작성하면 간단하게 사용 가능하다.

@GetMapping(PostMapping, PutMapping 등 다 상관없음){변수명}
메소드 정의에서 위에 쓴 변수명을 그대로 @PathVariable("변수명") 
(Optional) Parameter명은 아무거나 상관없음(아래에서 String name, String employName 둘 다 OK)

@RestController
public class MemberController { 
    // 기본
    @GetMapping("/member/{name}")
    public String findByName(@PathVariable("name") String name ) {
        return "Name: " + name;
    }
    
    // 여러 개
    @GetMapping("/member/{id}/{name}")
	public String findByNameAndId(@PathVariable("id") String id, 
    							  @PathVariable("name") String name) {
    	return "ID: " + id + ", name: " + name;
    }
    
}

@RequestParam

URL에 전달되는 파라미터를 메소드의 인자와 매칭시켜, 파라미터를 받아서 처리할 수 있는 Annotation으로 아래와 같이 사용한다. Json 형식의 Body를 MessageConverter를 통해 Java 객체로 변환시킨다.

cf. JSON이란?

JSON(JavaScript Object Notation)은 Javascript 객체 리터럴(literal) 문법으로 구조화된 데이터를 표현하기 위한 문자 기반의 표준 포맷이다. 경량하다는 특징 덕분에 웹 애플리케이션에서 데이터를 전송할 때 일반적으로 사용하며, 저장할 때도 사용한다. XML형식에 비해 필요한 구조가 적고 Key : Value로 보통 존재한다.

@Controller                   // 이 Class는 Controller 역할을 합니다
@RequestMapping("/user")      // 이 Class는 /user로 들어오는 요청을 모두 처리합니다.
public class UserController {
    @RequestMapping(method = RequestMethod.GET)
    public String getUser(@RequestParam String nickname, @RequestParam(name="old") 
  	String age {
        // GET method, /user 요청을 처리
        // https://naver.com?nickname=dog&old=10
        String sub = nickname + "_" + age;
        ...
    }
}

@RequestParam과 @PathVariable의 사용

  • @PathVariable과 @RequestParam 어노테이션은 uri를 통해 전달된 값을 파라미터로 받아오는 역할을 한다.
  • @PathVariable은 값을 하나만 받아올 수 있으므로, 쿼리스트링 등을 이용한 여러 개 데이터를 받아올 때는 @RequestParam을 쓴다.

cf. 쿼리 파라미터와 PathVariable의 차이점

4. Command 객체에 대하여 설명하시오.

커맨드 객체(Command Object)란?

HttpServletRequest를 통해 들어온 요청 파라미터들을 setter 메서드를 이용하여,
객체에 정의되어있는 속성에 바인딩되는 객체

커맨드 객체는 보통 DTO를 의미하며, HttpServletRequest로 받아오는 요청 파라미터의 key 값과 동일한 이름의 속성들과 setter 메서드를 가지고 있어야 한다.

스프링이 내부적으로 HttpServletRequest와 커맨드 객체의 setter 메서드를 이용하여 알아서 바인딩 시켜준다.

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

cf. @ModelAttribute

@ModelAttribute 의 사용 위치에 따라 기능이 달라지는데, 크게 메서드명 위에 사용되는 경우와 파라미터 옆에 사용되는 경우로 나뉜다.

@ModelAttribute의 기능

커맨드 객체와 같이 요청 파라미터들을 객체 프로퍼티에 바인딩시켜준다. (@ModelAttribute를 생략해도 커맨드 객체를 이용해서 바인딩이 되는데, @RequestParam 생략해도 바인딩이 가능하다.)

스프링이 내부적으로 String이나 int 등은 @RequestParam으로 보고, 그 외의 복잡한 객체들은 @ModelAttribute가 생략되었다고 간주하기 때문이다. 하지만 그렇다고 무조건 생략하는 것은 위험하다. 스프링이 간단한 숫자나 문자로 전달된 요청 파라미터를 복잡한 객체로 변환할 수도 있기 때문이다.

파라미터 객체 옆에 @ModelAttribute를 사용하는 경우

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

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

@RequestParam의 경우 스프링의 기본 타입 변환 기능을 이용해서 요청 파라미터 값을 메서드 파라미터 타입으로 변환하는데, 만약 숫자 타입의 파라미터라면 문자열 타입으로 들어온 요청 파라미터의 타입 변환을 시도하고 실패하면 Http 400 Bad Request 응답이 클라이언트로 가게 된다. (이 경우, 친절하게 메시지를 보여주고 싶으면 org.springframework.beans.TypeMismatchException 예외를 처리하는 예외 리졸버를 추가해준다.)

하지만 @ModelAttribute의 경우 내부적으로 검증(Validation) 작업을 진행하기 때문에 setter 메서드를 이용하여 값을 바인딩하려고 시도하다가 예외를 만나도 작업이 중단되면서 Http 400 Bad Request 가 발생하지는 않는다. 타입 변환에 실패해도 작업은 계속되며 BindingException 타입의 객체에 담겨서 컨트롤러로 전달된다.

보통 등록이나, 수정을 처리하는 핸들러 메서드의 경우 다양한 검증을 실시해야 하고, 사용자의 입력 값에 오류가 있을 때에는 이에 대한 처리를 컨트롤러에게 맡겨야 한다. 따라서 @ModelAttribute를 통해서 폼의 정보를 전달 받는 경우 Errors 객체나 BindingResult 객체를 @ModelAttribute가 붙은 파라미터 바로 뒤에 선언해서 검증 처리를 실시한다.

Errors나 BindingResult는 자신의 바로 앞에 있는 파라미터 검증에서 발생한 오류들만 전달해주기 때문에 @Valid 나 @Validated, @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); 
}

출처: 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

profile
띵호와

0개의 댓글