[Docker] DB 스키마 확인, 변경

merci·2023년 6월 6일
0

Docker

목록 보기
2/4

SQL Error: 1054, SQLState: 42S22 에러

도커를 이용해서 DB를 만들었는데 서버에서 접근하려고 할때 SQL Error: 1054, SQLState: 42S22 에러가 발생합니다.
해당하는 칼럼을 못찾아서 에러가 발생한건데 DB를 생성한 도커의 스크립트를 확인해보면

CREATE TABLE todos (
  id BIGINT auto_increment primary key,
  user_id BIGINT not null,
  title varchar(100) not null,
  done boolean not null
);
INSERT INTO todos (user_id, title, done) VALUES
(1, '아침먹고 운동하기', false),
(1, '점심먹고 운동하기', false);

commit;

이렇게 잘 생성되어 있고 서버에서는 매핑을 하기 위해서 @Calumn 어노테이션을 사용했습니다.

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Entity
@Table(name = "todos")
@Builder
public class Todo {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "user_id")    
    private Long userId;

    private String title;
    
    @Column(columnDefinition = "TINYINT(1)")
    private boolean done;
}

그런데 도대체가 왜 요청만 하면 could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet 이라는 응답을 받게 되는걸까요.

그래서 디버깅을 하면서 역추적을 했습니다.

동일한 스크립트로 H2.DB를 이용해 테스트를 했을경우 서비스단의 문제는 아니었습니다.

@DataJpaTest
@ActiveProfiles("test")
public class TodoRepositoryTest {

    @Autowired
    private TodoRepository todoRepository;

    @Test
    @Transactional
    public void findAll_test() {
        List<Todo> todoList = todoRepository.findAll();
        Assertions.assertEquals(todoList.get(0).getId(), 1);
        Assertions.assertEquals(todoList.get(1).getTitle(), "점심먹고 운동하기");
    }
@WebMvcTest(TodoController.class)
@EnableAspectJAutoProxy
@MockBean(JpaMetamodelMappingContext.class)
@Import({WebSecurityConfig.class, MyValidAdvice.class})
public class TodoMockTest {

    @MockBean
    private TodoService todoService;

    @Autowired
    private MockMvc mockMvc;

    @Autowired
    private ObjectMapper om;

    List<Todo> todos;

    @BeforeEach
    public void setup() {
        todos = List.of(
                Todo.builder().id(1L).userId(1L).title("아침먹고 운동하기").done(false).build(),
                Todo.builder().id(2L).userId(1L).title("점심먹고 운동하기").done(false).build());
    }

    @Test
    @MyWithMockUser(id = 1L, username = "son", role = "USER")
    public void findAll_MockTest() throws Exception {
        // given
        Long id = 1L;
       given(todoService.findByUserId(id)).willReturn(todos);

        // when
        this.mockMvc.perform(
                        MockMvcRequestBuilders
                                .get("/todos"))
                // then
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(print())
                .andExpect(MockMvcResultMatchers.jsonPath("$.data[0].id").value(1))
                .andExpect(MockMvcResultMatchers.jsonPath("$.data[0].title").value("아침먹고 운동하기"))
                .andExpect(MockMvcResultMatchers.jsonPath("$.data[0].done").value(false));
    }

스크립트에 문제가 없다는것을 알았으니 DB에 문제가 있을거라는 생각이 들었습니다.
그래서 이번에는 도커의 스키마를 확인해보기로 합니다.



도커의 DB 스키마 확인

먼저 도커 컨테이너에 접속한 뒤에 mysql클라이언트를 실행해야 합니다.

docker ps

docker exec -it [container_id_or_name] bash


mysql에 접속한 뒤 사용할 db를 선택해 들어갑니다.

mysql -u [username] -p

Enter password: #패스워드

db를 선택하고 스키마를 확인했습니다.

show databases;

use metacoding;

desc todos;



스키마를 확인해보니 칼럼명이 userId로 되어 있는것을 확인했습니다.
처음 스키마를 만들때 userId로 만들었지만 스크립트를 user_id로 수정했는데 왜 도커의 db 스키마는 변하지 않았을까요.


볼륨 다시 만들기

원인을 더 알아보니 도커의 볼륨 사용이 문제였습니다.

도커는 변경사항이 생겼을때 이미지를 다시 빌드해서 변경사항을 적용시켜야 합니다.
그래서 실행할 때 매번 변경을 감지한 후 감지되면 새로운 이미지를 만들어 빌드하는 방법을 이용했습니다.

docker-compose up --build

하지만 볼륨을 이용하면 얘기가 달라집니다.

도커는 컨테이너 종료와 상관없이 데이터를 유지하기 위해 볼륨을 제공하는데 이 볼륨은 위 명령어를 통해서 빌드를 하더라도 볼륨의 데이터는 변경되지가 않습니다.
따라서 도커의 볼륨이 수정 전의 스크립트로 만들어졌기 때문에 에러가 발생하게 되었습니다.

이 문제를 해결하기 위한 첫번째 방법은 볼륨을 삭제하고 다시 만드는 것입니다.

docker-compose down -v

docker-compose up --build


정상적인 스크립트로 볼륨을 다시 만들고 나서 요청을 하면 문제없이 응답을 받게 됩니다.


볼륨 유지하고 스키마 변경

두번째 방법은 위에서 mysql클라이언트에 접속한 상태에서 스키마를 변경합니다.

아래 명령어를 통해서 DB의 스키마를 변경 후 요청하면 정상적인 응답을 받을 수 있습니다.

ALTER TABLE todos CHANGE user_id userId BIGINT;

profile
작은것부터

0개의 댓글