섹션 2. Database 조작하기

섹션 2. Database 조작하기

목표

  1. 디스크와 메모리의 차이를 이해하고, Database의 필요성을 이해

  2. MySQL Database를 SQL과 함께 조작

  3. 스프링 서버를 이용해 Database에 접근하고 데이터를 이용

  4. API의 예외 상황을 알아보고 예외를 처리

문제점

저번에 한 작업을 다시 하려고 서버를 켜니 기존에 저장했던 데이터가 모두 사라졌다! 왜 이런 일이 생겼을까?
서버는 당연하게도 이런 일이 생기면 안된다.
이러한 일을 알기 위해 컴퓨터의 부품들을 살펴보자.

  1. CPU -> 연산, 사람의 뇌!

  2. RAM -> 단기 기억장치

  3. DISK -> 장기 깅억장치

개발하고 있는 서버는 DISK에 있다. 서버를 실행시키면 DISK의 코드 정보가 RAM으로 복사된다. API가 실행되면 연산이 수행되며 CPU와 RAM을 왔다갔다 한다. 이 유저 정보는 메모리인 RAM에 저장된다. 프로그램이 종료되면 RAM에 있는 모든 정보가 사라진다. 그렇게 때문에 유저 정보가 사라진 것이었다.

서버에서는 어떻게 DISK에 저장할 수 있을까?

이럴 때 바로 DataBase를 사용한다!
데이터베이스란 데이터를 구조화 시켜 저장하는 일을 한다.
관계형 데이터베이스인 MySQL을 이용해서 서버의 효율을 늘려보자.

먼저 스프링이 데이터베이스에 접근할 수 있게 yml 파일을 설정해준다.

spring:
  datasource:
    url: "jdbc:mysql://localhost/library"
    username: "root"
    password: "~~~~~~"
    driver-class-name: com.mysql.cj.jdbc.Driver

jdbc를 이용해서 데이터베이스에 접근할 수 있게 해준다.

create table user
(
    id           bigint auto_increment,
    name         varchar(25),
    age        int,
    stocked_date date,
    primary key (id)
);

이제 UserController로 가서 POST API를 변경해보자.

@RestController
public class UserController {
  private final JdbcTemplate jdbcTemplate;
  public UserController(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;
}
  @PostMapping("/user")
  public void saveUser(@RequestBody UserCreateRequest request) {
    String sql = "INSERT INTO user(name, age) VALUES(?, ?)";
    jdbcTemplate.update(sql, request.getName(), request.getAge());
  }
}

이렇게 final 변수를 통해 생성자를 만들면 스프링이 알아서 jdbcTemplate를 넣어준다
값이 들어갈 부분은 각 데이터마다 다르기 때문에 ?로 처리해 줬고 아래의 update 문에서 name, age를 받아서 값으로 이용하도록 했다.

@GetMapping("/user")
public List<UserResponse> getUsers() {
  String sql = "SELECT * FROM user";
  return jdbcTemplate.query(sql, new RowMapper<UserResponse>() {
    @Override
    public UserResponse mapRow(ResultSet rs, int rowNum) throws SQLException {
      long id = rs.getLong("id");
      String name = rs.getString("name");
      int age = rs.getInt("age");
      return new UserResponse(id, name, age);
}
});
}

query를 사용해 select 쿼리를 날리고 resultset 객체에는 결과가 담겨있고 이 객체에 getLong, getString과 같은 메서드를 사용해서 실제 값을 가져올 수 있다.

3.도서관 사용자 이름을 업데이트 할 수 있다.
-> HTTP Method를 PUT로 설정하고 Path를 /user, Body를 JSON을 이용한다. JSON은 id와 name를 담는다.

4.도서관 사용자를 삭제 할 수 있다.
-> HTTP Method를 DELETE로 설정하고 Path를 /user, 쿼리를 사용해서 삭제되어야 하는 문자열 name을 사용한다.

@PutMapping("/user")
public void updateUser(@RequestBody UserUpdateRequest request) {
  String sql = "UPDATE user SET name = ? WHERE id = ?";
  jdbcTemplate.update(sql, request.getName(), request.getId());
}

@DeleteMapping("/user")
public void deleteUser(@RequestParam String name) {
  String sql = "DELETE FROM user WHERE name = ?";
  jdbcTemplate.update(sql, name);
}
public class UserUpdateRequest {
  private long id;
  private String name;
  public long getId() {
    return id;
}
  public String getName() {
    return name;
} }

이렇게 작성을 했고 잘 작동을 한다.
단, 한 가지 문제가 있다 만약 존재하지 않는 유저의 경우를 테스트해봐도 잘 처리되었다는 200 OK를 반환한다. 이를 위해 에러 처리를 해주겠다.

@PutMapping("/user")
  public void updateUser(@RequestBody UserUpdateRequest request) {
    String readSql = "SELECT * FROM user Where id = ?";
    boolean isUserNotExist = jdbcTemplate.query(readSql, (rs, rowNum) -> 0, request.getId()).isEmpty();
    if (isUserNotExist) {
      throw new IllegalArgumentException();
    }

    String sql = "UPDATE user SET name = ? WHERE id = ?";
    jdbcTemplate.update(sql, request.getName(), request.getId());
  }

  @DeleteMapping("/user")
  public void deleteUser(@RequestParam String name) {
    String readSql = "SELECT * FROM user Where name = ?";
    boolean isUserNotExist = jdbcTemplate.query(readSql, (rs, rowNum) -> 0, name).isEmpty();
    if (isUserNotExist) {
      throw new IllegalArgumentException();
    }

    String sql = "DELETE FROM user WHERE name = ?";
    jdbcTemplate.update(sql, name);
  }

}

에러를 잘 던져준다! 이렇게 우리는 유저에 관련된 기능을 다 만들었다.

하지만 지금 코드에는 문제가 있다. 한 클래스인 Controller에서 모든 기능을 유지하고 있고 이는 좋은 코드는 아니다. 다음 섹션에서 왜 모든 기능을 한 섹션에서 구현하면 안되는지 알아보고 스프링의 핵심 개념을 통해 이 문제를 해결해보자.

 

이번주 회고

: 어느 정도 알았다고 생각한 부분이라 가볍게 들었지만 부족한 점이 많아서 혼자 생각하는 시간이 많았다. 뒤의 JPA 부분이나 컨테이너 부분 쪽으로 가면 더 어려워지니 기초를 튼튼히 해야겠다는 생각을 했다. 데이터베이스 부분은 특히 친숙하지 않은 jdbc를 써서 sql을 바로 연결하는 부분이 재밌었다. 평소에는 해보지 않았던 방법이라 흥미가 있었고 더욱 좋은 툴로 사용할 수 있을 것 같다.

댓글을 작성해보세요.

채널톡 아이콘