2021 04 15
2021-04-15¶
Spring 흐름 배우기 (simple)¶
- 참고: https://github.com/next-step/spring-learning-test/tree/simple
- 요청/응답 흐름
- client <--> Spring MVC
- 클라이언트로 부터 받은 요청과 응답은 Spring MVC 모듈을 통해 처리
- 예시
- DB 접근 흐름도 - Spring JDBC <--> DB - DB에 대한 접근은 Spring JDBC로 처리 - Controller에서 DAO 사용하기 - HelloDao 객체를 Controller에서 직접 생성 X - "스프링 컨테이너"로 관리하기 위해 Controller에 DAO를 의존성 주입 - 생성자 주입을 통해 주입
- 그러면 Spring Core가 Controller에 대한 것인가?
- 생성도 안 한거 같은데 어찌 주입된거지?
- 스프링 컨테이너는 또 뭐야?
Spring MVC 학습 테스트¶
- 참고: https://github.com/next-step/spring-learning-test/tree/mvc
- mapping
- "요청을 메서드에 연결"
1. HttpMethod
```java
@RestController //클라이언트의 요청을 처리해줄 클래스에요!
@RequestMapping("/http-method/users") //저는 해당 URL로의 요청을 처리해줘요!
//근데 이 어노테이션은, URL, HTTP 메서드, request param, header, media type에 따라 요청을 처리해주기도 해요!
public class HttpMethodController {
@PostMapping //RequestMapping의 변형이에요! 저는 Post 방식을 받아들여요! public ResponseEntity createUser(@RequestBody User user) { Long id = 1L; return ResponseEntity.created(URI.create("/users/" + id)).build(); } @GetMapping //RequestMapping의 변형이에요! 저는 Get 방식을 받아들여요! public ResponseEntity<List<User>> showUser() { List<User> users = Arrays.asList( new User("이름", "email"), new User("이름", "email") ); return ResponseEntity.ok().body(users); } } ```2. MediaType
@RestController //클라이언트의 요청을 처리해줄 클래스에요! @RequestMapping("/media-type/users") //저는 해당 URL로의 요청을 처리해줘요! public class MediaTypeController { //저는 Post 방식으로 들어온 JSON을 받아들여요! //해당 속성을 통해 사용자가 Request Body에 담는 타입을 제한할 수 있어요! //Client가 Post방식으로 요청을 보낼 때, JSON 타입으로 요청을 보내야 정상 실행될거에요! @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity createUser(@RequestBody User user) { Long id = 1L; return ResponseEntity.created(URI.create("/users/" + id)).build(); } //저는 Get방식으로 요청이 들어오면, JSON 형식으로 데이터를 만들어 반환해요! //Client가 http 요청에 Accept헤더에 JSON을 받는다고 명시를 한다면, 저는 실행될 것이에요! //JSON 형식을 받는다고 클라이언트가 요청했으니, JSON을 만들어 뚝딱 반환하는 제가 실행됩니다. @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<List<User>> showUser() { List<User> users = Arrays.asList( new User("이름", "email"), new User("이름", "email") ); return ResponseEntity.ok().body(users); } //저는 Get방식으로 요청이 들어오면, HTML 형식으로 데이터를 만들어 반환해요! //Client가 http 요청에 Accept헤더에 HTML을 받는다고 명시를 한다면, 저는 실행될 것이에요! //HTML 형식을 받는다고 클라이언트가 요청했으니, HTML을 만들어 뚝딱 반환하는 제가 실행됩니다. @GetMapping(produces = MediaType.TEXT_HTML_VALUE) public String userPage() { return "user page"; } }3. ParamHeader
@RestController @RequestMapping("/param-header/message") public class ParamHeaderController { @GetMapping() public ResponseEntity<String> message() { return ResponseEntity.ok().body("message"); } //만일 Get 요청이 지정한 param으로 들어온다면, 즉 쿼리스트링(?name=hello)으로 들어온다면 이걸 실행해 주세요! @GetMapping(params = "name=hello") public ResponseEntity<String> messageForParam() { return ResponseEntity.ok().body("hello"); } //만일 Get 요청이 header로 들어온다면, better=hi로 들어온다면 이걸 실행해줘요! @GetMapping(headers = "better=hi") public ResponseEntity<String> messageForHeader() { return ResponseEntity.ok().body("hi"); } }4. UriPattern
//URI의 패턴을 정규표현식+정해진 문법으로 지정할 수 있음! @RestController @RequestMapping("/uri-pattern") public class UriPatternController { @GetMapping(path = "users/{id}") public ResponseEntity<User> pathVariable(@PathVariable Long id) { User user = new User(id, "이름", "email"); return ResponseEntity.ok().body(user); } //하나의 영문자가 들어온다면 실행해주세요! @GetMapping("patterns/?") public ResponseEntity<String> pattern() { return ResponseEntity.ok().body("pattern"); } //뒤에 무엇이 붙은다면 이걸 실행해주세요! @GetMapping("patterns/**") public ResponseEntity<String> patternStars() { return ResponseEntity.ok().body("pattern-multi"); } }
- handler
- "요청을 처리하는 부분"
1. MethodArgument
@RestController @RequestMapping("/method-argument") public class MethodArgumentController { @GetMapping("/users") //파라미터로 넘어온 친구가, 즉 쿼리스트링으로 넘어온 친구가 ?name=*** 형식이라면, //***를 userName에 저장해주세요! public ResponseEntity<List<User>> requestParam(@RequestParam("name") String userName) { List<User> users = Arrays.asList( new User(userName, "email"), new User(userName, "email") ); return ResponseEntity.ok().body(users); } @PostMapping("/users/body") //RequestBody 어노테이션을 통해서, request body로 들어온 친구를 역직렬화해서 객체로 만들수 있어요! public ResponseEntity requestBody(@RequestBody User user) { User newUser = new User(1L, user.getName(), user.getEmail()); return ResponseEntity.created(URI.create("/users/" + newUser.getId())).body(newUser); } }2. ReturnValue
@RestController @RequestMapping("/return-value") public class ReturnValueController { @GetMapping("/message") //Client가 .accept(MediaType.TEXT_HTML_VALUE)라고 했다면, RequestBody를 통해 //<html> // <body>message</body> //</html> //를 반환! @ResponseBody public String string() { return "message"; } @GetMapping("/users") //Client가 .accept(MediaType.APPLICATION_JSON_VALUE)라고 했다면, RequestBody를 통해 //{ // "id": null, // "name": "name", // "email": "email" //} //를 반환! @ResponseBody public User responseBodyForUser() { return new User("name", "email"); } //ResponseEntity를 통해서 return value를 full response로 변경! (HTTP 헤더등을 포함) //ResponseBody와 비슷하지만, status와 header를 추가할 수 있음 //참고: https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-responseentity @GetMapping("/users/{id}") public ResponseEntity responseEntity(@PathVariable Long id) { return ResponseEntity.ok(new User("name", "email")); } @GetMapping("/members") public ResponseEntity responseEntityFor400() { return ResponseEntity.badRequest().build(); } }
- exception
- "요청에 대한 예외처리"
@RestController @RequestMapping("/exceptions") public class ExceptionsController { //해당 Exception이 발생하면 이걸 던져주세요! @ExceptionHandler({CustomException.class}) public ResponseEntity<String> handle() { return ResponseEntity.badRequest().body("CustomException"); } } @ControllerAdvice //Controller 전역에서 발생한 예외를 잡아와서 처리하는 클래스! public class HelloAdvice { @ExceptionHandler(HelloException.class) public ResponseEntity<String> handle() { return ResponseEntity.badRequest().body("HelloException"); } }
Spring JDBC 학습 테스트¶
- 참고: https://github.com/next-step/spring-learning-test/tree/jdbc
1. NamedParamDAO
@Repository public class NamedParamDAO { private NamedParameterJdbcTemplate namedParameterJdbcTemplate; public NamedParamDAO(NamedParameterJdbcTemplate namedParameterJdbcTemplate) { this.namedParameterJdbcTemplate = namedParameterJdbcTemplate; } /** * MapSqlParameterSource * public <T> T queryForObject(String sql, SqlParameterSource paramSource, Class<T> requiredType) */ public int useMapSqlParameterSource(String firstName) { String sql = "select count(*) from customers where first_name = :first_name"; SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName); return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class); } /** * BeanPropertySqlParameterSource * public <T> T queryForObject(String sql, SqlParameterSource paramSource, Class<T> requiredType) */ public int useBeanPropertySqlParameterSource(Customer customer) { String sql = "select count(*) from customers where first_name = :firstName"; SqlParameterSource namedParameters = new BeanPropertySqlParameterSource(customer); return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class); } }2. QueryingDAO
@Repository public class QueryingDAO { private JdbcTemplate jdbcTemplate; public QueryingDAO(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } private final RowMapper<Customer> actorRowMapper = (resultSet, rowNum) -> { Customer customer = new Customer( resultSet.getLong("id"), resultSet.getString("first_name"), resultSet.getString("last_name") ); return customer; }; /** * public <T> T queryForObject(String sql, Class<T> requiredType) */ public int count() { String sql = "select count(*) from customers"; return this.jdbcTemplate.queryForObject(sql, Integer.class); } /** * public <T> List<T> query(String sql, RowMapper<T> rowMapper, @Nullable Object... args) */ public List<Customer> findCustomerByFirstName(String firstName) { String sql = "select id, first_name, last_name from customers where first_name = ?"; return this.jdbcTemplate.query(sql, actorRowMapper, firstName); } }3. UpdatingDAO
@Repository public class UpdatingDAO { private JdbcTemplate jdbcTemplate; public UpdatingDAO(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } /** * public int update(String sql, @Nullable Object... args) */ public void insert(Customer customer) { String sql = "insert into customers (first_name, last_name) values (?, ?)"; this.jdbcTemplate.update(sql, customer.getFirstName(), customer.getLastName()); } /** * public int update(String sql, @Nullable Object... args) */ public int delete(Long id) { String sql = "delete from customers where id = ?"; return this.jdbcTemplate.update(sql, id); } }4. SimpleInsertDAO
@Repository public class SimpleInsertDao { private SimpleJdbcInsert insertActor; public SimpleInsertDao(DataSource dataSource) { this.insertActor = new SimpleJdbcInsert(dataSource) .withTableName("customers") .usingGeneratedKeyColumns("id"); } /** * Map + * insertActor.executeAndReturnKey */ public Customer insertWithMap(Customer customer) { Map<String, Object> parameters = new HashMap<>(3); parameters.put("first_name", customer.getFirstName()); parameters.put("last_name", customer.getLastName()); Long id = insertActor.executeAndReturnKey(parameters).longValue(); return new Customer(id, customer.getFirstName(), customer.getLastName()); } /** * SqlParameterSource + * insertActor.executeAndReturnKey */ public Customer insertWithBeanPropertySqlParameterSource(Customer customer) { SqlParameterSource parameters = new BeanPropertySqlParameterSource(customer); Long id = insertActor.executeAndReturnKey(parameters).longValue(); return new Customer(id, customer.getFirstName(), customer.getLastName()); } }
- @Autowired는 뭐야?
- JdbcTemplate VS NamedParameterJdbcTemplate
- queryForObject VS query VS update
- RowMapper란?
- executeAndReturnKey?
- BeanPropertySqlParameterSource?
Spring Core 학습 테스트¶
- 참고: https://github.com/next-step/spring-learning-test/tree/core 1. DI - 생성자 주입 - 필드 주입 - Setter 주입 2. 스프링 빈으로 등록하기 - @Component - @Repository - @Controller - @Service