인공지능 웹서비스 팀프로젝트 둘째 주 회고 : 도커, TDD

Ji_min·2021년 5월 23일
0

프로젝트 회고

목록 보기
1/2

1. 드디어 개발 시작!

저번 주에는 기획 위주로 진행을 했다면, 이번 주부터는 본격적으로 기능 개발에 들어갔다. 기획할 때도 정말 머리가 터질 것 같았지만, 개발 주간에 들어와서도 머리 아프게 생각해야 할 일이 많았다. 특히 이번에는 5주라는 긴 시간도 있고 엘리스에서 하는 마지막 프로젝트인 만큼 해보고 싶은 건 다 해보자는 생각에 이것저것 다양한 것들을 시도해보기로 해서 공부하고 고민할 게 저번보다 훨씬 많아진 것 같다.

이번 한 주 동안 새롭게 배운 내용과 생각지 못한 여러 에러를 마주하며 해결해간 과정을 돌이켜보고자 한다.

2. Dockerized Development : 도커로 개발하기

지난 프로젝트 때 도커를 사용해봤는데, 그때는 도커를 잘 아시는 팀원이 계셔서 그분이 도커 파일을 만들어오시고 사용 방법을 다 알려주셨다. 그래서 그때는 도커를 뭔가 알고 사용한다기 보다는 아무것도 모르면서 그냥 시키는 대로 명령어를 입력해서 작업을 했고, 왜 도커를 써야 하는지, 컨테이너 환경에서 작업하고 배포하는 게 무슨 이점이 있는지 제대로 알 지도 못한 상황이었기 때문에 도커를 안 써도 그만 써도 그만인 입장에서 쓰자는 다수의 의견에 그저 탑승한 것에 지나지 않았다.

그게 너무 아쉬워서 이번에는 도커가 뭐고 쓰면 뭐가 좋은 건지 공부했고, 개발 환경과 배포 환경의 일관성을 유지하는 게 개발 외의 시간에 투입되는 에너지 소모를 줄일 수 있다는 점을 들어 팀원들을 설득해서 팀 전체가 도커 환경에서 작업해보기로 결정했다.

도커 파일과 도커 컴포즈 파일을 만드는 과정은 Django+PostgreSQL 레퍼런스가 있어서 수월했는데, 문제는 그 다음부터였다.

문제 1. 컨테이너 안에서 어떻게 개발할까?

백엔드 디렉토리에서 도커 파일과 도커 컴포즈 파일을 만들고 docker-compose up --build -d 명령을 실행시켰다.

그런데 VSCode로 생성된 장고 앱 컨테이너에 접속해서 개발을 하려고 하니 여기서 파일 수정을 한 후 어떻게 원격 저장소에 push를 할 지가 고민이었다. 장고 컨테이너에는 백엔드 디렉토리의 파일밖에 없는데, 얘를 바로 저장소에 올리면 프론트엔드의 파일과 루트 디렉토리의 파일들은 어떻게 되는 거지?? 하는 생각이 들었다.

답은 간단했다. volume 옵션을 주고 로컬의 백엔드 디렉토리와 컨테이너의 디렉토리를 연결해놓으면 컨테이너에서 작업한 사항이 바로 로컬에 반영이 된다! volume을 데이터 백업 용으로만 생각했는데, 소스 코드에도 적용이 된다. 정말 신기하고 도커는 알면 알수록 재미있는 것 같다.

그래서 현재의 작업 플로우는 다음과 같다.

도커 컨테이너 실행하기 → 장고 컨테이너에 vscode 접속 → 작업 → 로컬로 돌아와서 수정 사항 commit 후 원격 저장소로 push → 컨테이너 종료

문제 2. DB 컨테이너와 장고 컨테이너를 어떻게 연결시킬까?

장고 컨테이너와 postgresql 컨테이너 두 개를 띄워 놓고 서로 연결시키려고 했는데,

SQLSTATE[08006] [7] could not connect to server: Connection refused
Is the server running on host “0.0.0.0” and accepting
TCP/IP connections on port 5432? 

이런 에러가 뜨면서 연결이 되지 않았다.

도커 컴포즈 파일의 환경 변수를 바꿔보고 포트도 바꿔 보는 등 여러 가지를 시도했는데도 연결이 안 되어서 한참 해결책을 찾아보았다.

그러다가 .env 파일의 DB host 값을 바꿔주었더니 해결이 되었다. DB host를 DB 컨테이너 이름으로주면 알아서 컨테이너 서버를 인식하는 것 같다.

DATABASE_HOST=db   # 얘를 postgresql 컨테이너 이름으로 주면 된다. 

문제 3. 갑자기 이미지 빌드가 안된다?

새로운 패키지를 설치한 다음 docker-compose up --build 명령어를 실행했는데, 갑자기 다음과 같은 에러가 뜨면서 컨테이너 이미지가 만들어지지 않았다.

ERROR [internal] load metadata for docker.io/library/python:3 

찾아보니 해결책이 제각각이었다. 누구는 도커 허브에 회원가입하고 테미널에서 인증하니 해결이 되었다는 사람도 있었고, 그냥 아예 터미널을 종료하고 재시작하니 되었다는 사람도 있었다. 나는 전자를 따라해보았다.

  1. docker hub 회원가입
  2. docker login 후 사용자 정보 입력
  3. 다시 docker-compose 명령 실행
  4. 작동 잘 됨!

해결이 되었다.

문제 4. 도커 컴포즈 파일에서 민감한 정보를 어떻게 숨겨야 할까?

도커 컴포즈 파일에서 DB 컨테이너를 만들 때 환경 변수로 DB 패스워드를 넘겨주는데, 얘도 비밀번호인 이상 민감한 정보라고 생각해서 환경 변수를 파일로 만들어 감춰버리는 것처럼 숨기고 싶었는데, 어떻게 할 지 찾아보다가 두 가지 방법을 발견했다.

  1. docker secret 만들기 : docker secret은 docker의 특정 경로에 secret 객체를 생성해서 비밀번호를 저장하는 방법
  2. env file 만들기 : 환경 변수 파일을 만들어서 도커 컴포즈 파일에서 그 경로를 지정하는 방법

그런데 생각해보니 DB 컨테이너를 개발용으로밖에 사용하지 않을 것 같아서 그냥 그대로 올리기로 했다.

3. Get TDD With Django : 장고로 TDD 하기

장고로 API를 만들면서 Django Rest Framework에서 제공하는 API Test Case가 있길래, 좋은 기회라고 생각해서 TDD를 시도해보았다.

그런데 뭔가 이상했다. TDD가 실패하는 코드에서 시작해 성공하는 코드를 만들어가는 과정이란 것은 알고 있지만, 아무리 코드를 빌드해도 성공이 뜨지 않았다. 분명 DB를 까보면 데이터가 존재하는데, 테스트 코드를 실행시키면 계속 matching query does not exist 라는 에러 로그가 떴다. api url이 잘못되었나 싶어서 확인해보니 그것도 아니었고, runserver를 통해 api url로 접속해보아도 잘 접근이 되었는데, 테스트 코드를 돌리기만 하면 실패가 떠서 도대체 뭐가 문젠지 알 수 없었다.

그래서 일단 DB에서 데이터를 잘 가져오는지 확인하는 테스트 코드를 짜서 돌려봤는데, 얘도 실패했다. 이상해서 찾아보니, 장고(가 상속하는 unittest)는 테스트 코드를 돌릴 때 테스트 DB를 따로 만들기 때문에 아래 코드처럼 setUp에서 따로 데이터를 만들어 줘야 했다. 나는 만들어주지 않고 데이터를 가져오라고 시켰기 때문에 내 코드는 빈 DB에서 데이터를 가져오려고 한 것이고, 그래서 계속 404 NOT FOUND 가 떴던 것이다.

class TestUserProfileView(APITestCase):
    # 여기서 model instance를 생성해줘야 한다. 
    def setUp(self):
        self.url = reverse('mypage:profile', kwargs={'user_id':1}) 
        self.user = TestUser.objects.create(name='marina') 
        self.user2 = TestUser.objects.create(name='kevin') 

    def test_get_profile(self):
        response = self.client.get(self.url) 
        serializer = UserSerializer(self.user)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data, serializer.data)

그래서 데이터를 만들어줬다. 그리고 다시 테스트를 돌려봤는데, 또 실패했다! 💢

도대체 문제가 뭔지... 또 한참 찾았다. 너무 답답해서 해당 request 데이터에 해당하는 애를 콘솔에 찍어봤는데, pk가 2가 나오는 것이었다...! 나는 얘 하나밖에 안 만들어줬는데!?? ❓

이상해서 다른 애를 하나 더 만들고 또 찍어봤는데, 이번에는 똑같은 애의 pk가 3이 나왔다! 아까는 2였는데...??? 또 다른 애를 하나 더 만들어보니 아까는 3이었던 애의 pk가 이번에는 4가 되어 있었다. 이해가 안 되어서 찾아보니, django.test.TestCase의 TestCase가 아니라 unittest.TestCase의 TestCase를 상속받아서 사용하면 모종의 이유로 pk의 일관성을 유지하지 못한다는 것 같았다. DRF의 APITestCase가 unittest의 TestCase를 상속받나 보다. 테스트를 몇 번 해본 결과 내가 생성한 데이터의 수만큼 pk가 offset되는 것 같다.

class TestUserProfileAPIView(APITestCase):
    def setUp(self):
    # 생성한 데이터의 수만큼 pk가 offset되기 때문에 user1의 pk는 3이고 user2의 pk는 4가 된다. 
        self.url = reverse('mypage:profile', kwargs={'user_id':3}) 
        self.user1 = TestUser.objects.create(name='marina') # pk=3
        self.user2 = TestUser.objects.create(name='kevin') # pk=4

TransactionTestCase를 상속받아 사용하면 pk가 유지된다고 하는데, 일단은 여기까지만 이해해도 앞으로 테스트 코드를 작성하는데 무리는 없을 것 같아서 그대로 진행했다. 이미 이 문제 때문에 하루 반나절 가량의 시간과 거기에 상응하는 에너지를 소모했기 때문에 더 알아볼 기력이 없었다. 테스트할 때 pk를 유지하는 방법에 대해서는 다음에 더 자세히 공부해봐야 할 것 같다.

4. Gather at Gather Town : 우리도 이제 Gather 써요

팀원들에게 게더를 써보자고 건의해서 다 같이 스크럼을 게더에서 하게 되었다.

도입 결과 다들 만족도가 크다고 하셨다. 일단 캐릭터와 맵이 아기자기하고 귀엽고, 실제로 서로가 만나는 느낌이 있어서 실제로 오프라인 상에서 협업을 하는 것 같았다.
(루프탑에서 진행한 스크럼... 분위기 최고!)


테트리스 게임도 같이 해보고, 캐치 마인드도 하면서 즐거운 시간을 보냈다. 거기서 바로 만나서 대화하기에도 편했다. 앞으로도 잘 사용할 것 같다. 👍

5. 셋째 주를 준비하며

일단 팀이 기획한 서비스의 큰 틀을 이번 주에 거의 구현을 해놓았기 때문에, 다음 주는 이제 Mock 데이터를 넣어보거나 인공지능 모델을 돌려보는 작업을 하려고 한다. 아직 주어진 시간이 꽤나 많이 남은 만큼, 더 다양한 것들을 시도해보고 싶다.

일단은 API 문서화를 위한 도구인 drf-yasg 패키지를 설치해놓았는데, 얘를 통해 만들어진 api 문서 페이지를 더 보기 좋게 꾸며 보려고 한다.

또 다음주부터는 본격적으로 젠킨스를 공부해서 Dockerized CI/CD 환경을 구성해보려고 한다.

다음 주도 화이팅! 💪

profile
Curious Libertine

0개의 댓글