콘텐츠로 이동

2021 04 15

2021-04-15

Spring 흐름 배우기 (simple)

  • 참고: https://github.com/next-step/spring-learning-test/tree/simple
  • 요청/응답 흐름 - client <--> Spring MVC - 클라이언트로 부터 받은 요청과 응답은 Spring MVC 모듈을 통해 처리

    - 예시

    @RestController //클라이언트의 요청을 처리해줄 클래스에요!
    public class HelloController {
    
        @GetMapping("/hello") //"/hello"요청이 오면 저에게 알려주세요!
        public String hello(@RequestParam(defaultValue = "") String name) {
            return "Hello " + name;
        }
    }
    

  • 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 학습 테스트