본문 바로가기
기록해보기

Spring에서 Database 사용하기

by titlejjk 2023. 6. 3.

지금까지 내가 직접적으로 MySQL에 직접접근하여 테이블도 만들고 조작도 해보았는데 이번에는 Spirng boot를 통해 MySQL을 조작해보겠다. 이를 위해서는 설정이 필요하다.

이 설정은 application.yml을 만들고 설정할 수 있다.

src 안에 main 안에 java가 있고 그 안에 classe들이 들어가는데 java랑 같은 위치에 resources가 있는데 여기에 application.yml을 만들어줄 것이다.

강의에서는 나뭇잎모양 뜨던데 나만 안뜬다..

이제 여기안에 spring boot가 데이터베이스와 붙게 끔 코드 작업을 해 주겠다.

spring:
	datasource : //datasource라는 것은 spring boot가 어떠한 데이터베이스를 가르키게 할 것인가?라는의미
    	ur l: "jdbc:mysql://localhost/library" //spring이 붙을 database의 주소

여기서 url을 잠깐 살펴보자면 jdbc라는 것은 java database connector라는 뜻으로 연결할수 있게끔 프로그램을 사용할 것인데 이때 데이터베이스의 종류는 MySQL이고 사용할 데이터베이스의 컴퓨터 주소는 localhost 그리고 데이터베이스는 library 라는 뜻이다.

그 다음 접속하기 위한 접속 정보도 필요하기에 username과 password를 적어준 후에 driver-class-name을 적어주는데 이 것은 데이터베이스를 접근할 때 필요한 프로그램이다.

정리하자면

url: "jdbc:mysql://localhost/library" 👉 Spring이 연결할 데이터주소를 나타내는데 jdbc:mysql://는 -jdbc를 이용해 mysql에 접근한다, localhost는 접근하려는 mysql이 localhost(나의컴퓨터)이고 library는 데이터베이스(MySQL안에 있는 데이터베이스)를 나타낸다.

username 👉 MySQL에 접속하려는 계정명

password 👉 MySQL에 접속하려는 계정의 비밀번호

driver-class-name 👉 com.mysql,cj.jdbc.Driver 데이터베이스에 접근할 때 사용할 프로그램

 

이렇게하면 Spring이 MySQL에 접속할수 있는 환경을 만들어주었다.

이제 데이터베이스에 user테이블을 만들어보겠다.

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

user같은 경우에는 기본적으로 고유한 번호를 부여할 id, user의 이름 그리고 나이를 만들어 보겠다.

터미널을 열어 mysql -u root를 입력해서 mysql을 열어준 후에 위에 명령어를 입력해서 테이블을 만들어보자.

계속되는 오류..

MySQL을 오늘 처음 접해봐서 그런지 적응이 안되서 그런지..데이터베이스 안으로 들어가는 use명령어를 안쓰고 만드니 저렇게 No database selected가 뜬다..다시..

use library로 들어가주고 show tables로 만들어져있는 테이블도 만들어주고 확인 뒤에 다시 생성을 했다!

이제 UserController로 들어가서 memory에 저장하던 것을 MySQL에 저장이 되도록 바꿔줄 것이다. 이때 인텔리제이의 단축키를 사용하면 조금더 빠르게 클래스를 찾아들어갈 수 있다. 맥에서는 ⌘⇧O라는 단축키를 이용해 Class를 찾아 들어가면 쉽다.

 

이제 이 UserController에서 하나하나 바꾸어 주겠다 먼저

메모리에 저장하고 있던 List<User> users = new ArrayList<>(); users객체를 지워준 후에

private final JdbcTemplate jdbcTemplate;

를 입력해준다. 이것은 jdbc에 대한 Class인데 이것을 통해서 데이터베이스에 접근이 가능해진다.

현재 빨간줄이 뜨는데 이를 막기위해서

public UserController(JdbcTemplate jdbcTemplate){
        this.jdbcTemplate = jdbcTemplate;
    }

로 연결해주어야한다.

위와 연결해서 설명해보자면 jdbcTemplate을 선언하고 jdbcTemplate을 받아서 설정해주는 생성자를 만들어 준 것이다. 이렇게 되면 

jdbcTemplate이란 것을 생성자에 직접 넣어주지 않더라도 Spring이 알아서 jdbcTemplate을 넣어준다.

이부분에 대한 설명은 다음에 해보겠다.

다음으로 저장하는 부분을 만들어 보겠다. 

 @PostMapping("/user") //POST /user
    public void saveUser(@RequestBody UserCreateRequest request){
        String sql = "INSERT INTO user (name, age) VALUES (?, ?)";
        jdbcTemplate.update(sql, request.getName(), request.getAge());
    }

VALUES(?, ?)에는 다음과 같이 적용이된다.

첫번째 물음표에는 request.getName()

두번째 물음표에는 request.getAge()

 

여기까지 중간 정리를 해보자면

1. jdbcTemplate를 이용하면 저장할 sql을 MySQL로 보낼수 있다.

2. 이 jdbcTemplate를 사용하기 위해서 필드를 선언해주고 생성자를 만들어 주었는데 이를 통해 jdbcTemplate이 자동으로 파라미터를 통해 들어와 설정되게 된다.

3. SQL을 만들어서 문자열 변수로 저장해서 만든다. 

물음표로 만든이유는 값을 유동적으로 넣을 수 있기 때문이다.

4. 데이터를 저장하기 위해서 jdbcTemplate.update를 사용하였는데 이 때 사용한 update는 데이터를 수정하기 위해서 사용한 update문이 아니라 데이터의 변경이 일어난다는 것을 의미하기 때문에 생성이나 수정, 삭제 쿼리 모두 사용할 수 있다. 그렇기 때문에 이 jdbcTemplate.update는 첫 파라미터로 sql을 받고 ?를 대신할 값을 차례대로 넣어주면 된다.

 

다음으로 GET API를 변경하러 가보겠다

@GetMapping("/user")
    public List<UserResponse> getUser(){
        List<UserResponse> responses = new ArrayList<>();
        for(int i = 0; i < users.size(); i++){
            responses.add(new UserResponse(i + 1, users.get(i)));
        }
        return responses;
    }

원래는 가지고 있던 user를 UserResponse로 바꾸어 주었는데  이것을 SQL로 바꾸어 주어야한다.

@GetMapping("/user")
    public List<UserResponse> getUser(){
        
    }

코드를 다지우고..

user Table을 모두 가져오는 

String sql = "SELECT * FROM user";

를 작성해주고 그다음에 

jdbcTemplate.query(sql, new RowMapper<UserResponse>(){})

를 작성해준뒤 mapRow라는 메소드를 @Override해준다. 이 mapRow라는 메소드는 jdbcTemplate의 쿼리가 이 들어온 sql을 수행한다. 그러면 user정보들이 담기는데 이 정보를 선언한 타입인 UserResponse로 바꿔주는 역할을 수행하는 메소드이다.

그 다음 Override된 mapRow메소드에는 다음과 같이 구현부에 작성해준다.

@GetMapping("/user")
    public List<UserResponse> getUser(){
        String sql = "SELECT * FROM user";
        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 UserResponse(id, name, age);
            }
        });
    }

생성자를 만들러..

id같은 경우에는 rs (result set)이 결과인데 결과에서 LongType변수를 가져오고 그 이름은 "id"다

name 의 경우는 rs에서 getString 즉 String Type으로 가져오고 그 이름은 "name"이다.

마찬가지로 age는 rs에서 getInt Int Type가져오고 그 이름은 "age"이다 라는 로직을 작성해준다.

UserResponse를 반환하기 때문에 UserResponse의 id랑 name이랑 age를 반환하겠다라는 return도 작성해준다.

이렇게 구현부에 코드를 작성해준 후에 UserResponse Class에가서 id, name, age의 생성자를 만들어주어야한다.

다시 UserController Class로 가 UserResponse앞에 new를 붙여준다.

 

이렇게 작성해주면 mapRow메소드가 주어진 sql 즉 "SELECT * FROM user"를 수행해서 나온 결과들을 작성한 로직에 따라서 UserResponse로 바꿔준다. 이 바꿔준 값을 return해주면 된다.

@GetMapping("/user")
    public List<UserResponse> getUser(){
        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);
            }
        });
    }

조금 어려운 부분이지만 정리를 해보자면

jdbcTemplate.query(sql, RowMapper 구현 익명클래스)를 사용하고 그 다음에 입력했던 sql을 넣어주고 new RowMapper를 오버라이드 해준다. 이것은 자바 문법적으로 익명클래스가 된다.

그래서 이 mapRow안에서 sql실행 결과가 나오면 그 결과들에 있는 데이터를 가져와서 우리가 원하는 UserResponse로 바꿔주는 코드가 되겠다.

그래서 이 RowMapper는 쿼리의 결과를

받아 원하는 객체를 반환해주는 역할을 해주고 있고

이 때 주어진 ResultSet 에 getType("필드이름")을 사용해 실제 값을 가져오게 된다.

그다음 UserResponse(id, name, age);에 생성자를 추가해서 DB에서 조회해온 user정보를 UserResponse로 바꿔서 결과를 반환하게 해주고 덕분에 전체적으로 모든 유저정보를 하나씩 UserResponse로 바꿔서 리턴하게 되면 List<UserResponse>가 나오게 된다.

위 코드같은 경우에는 익명클래스를 더 간결하게 하기 위해 문법 람다식으로 가능하다.

람다식으로 바꿔보기

RowMapper를 드래그 해준 후에 ⌥ + ↩︎ 

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

를 하면 위 처럼 간결한 코드가 나오게된다. 아직 이부분은 익숙하지 않아서 조금더 공부해야겠다..

이렇게 원래 memory에 저장되던 부분을 이제 MySQL에 저장되게 끔 바꿔보았다.

 

이제 web ui에서 테스트를 하러가보겠다 아무 이름이나 나이를 입력해보겠다.

깊은 빡침..

지금 새벽 4시44분이라 이것만 공부하고 자려고했는데 오류를 찾아보고 자야겠다..우선 오류코드를 찾아보니

org.h2.jdbc.JdbcSQLSyntaxErrorException Exception이 발생됬다.

강사님 커뮤니티와 구글링을 해보니 H2와 관련된 에러라고 하신다.

현재 MySQL에 SQL쿼리가 나가고 있지 않고, H2에 SQL쿼리가 나가고 있는 오류인데 Spring에서는 데이터베이스가 설정되지 않으면 H2를 기본으로 설정하게 되어서 발생되는 오류라고 하신다. 때문에 MySQL설정이 제대로 되지 않은 것이여서 오류가 발생되었다.

 

강사님이 내주신 해결책

1. MySQL과 연결되어 있지 않고 H2 DB와 연결되어 있는 것은 자명하므로, MySQL설정이 제대로 된것인지 확인먼저 해보자.

  • resources 폴더 안에 application.yml이 잘 들어있는지 확인
  • application.yml파일 안에 DB관련 설정이 정상적으로 들어가 있는지 확인

첫번째 부터 확인해보면

잘들어가 있고..

잘쓴거같은데 혹시나 해서 : 를 강사님이 써주신것 처럼 다 앞쪽에 붙여보았다.

바로 져버리네..

2.위의 두 설정을 모두 잘 했는데도 MySQL연결이 되고 있지 않다면, 현재 인텔리제이에서 실행되는 스프링이 resources 폴더를 리소스 폴더로 인지하지 못하는 경우

라고 하셨는데 방금 문제점을 발견했다.

 

^^7

이름을 다시 application으로 변경해주고 다시 실행해보겠다..

매번 느끼는 거지만 진짜 컴퓨터는 잘못없고 모두 내 잘못이다..

내일 이부분은 다시 복습해봐야겠다..

댓글