inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

섹션 2. Database 조작하기

채 수
0

목표

  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을 바로 연결하는 부분이 재밌었다. 평소에는 해보지 않았던 방법이라 흥미가 있었고 더욱 좋은 툴로 사용할 수 있을 것 같다.

인프런워밍업클럽

답변 0