일반

chatGPT와 함께 지뢰찾기(minesweeper) 만들어보기 (1)

leesche 2024. 4. 14. 16:21

추억의 지뢰찾기

chatGPT와 차근차근 지뢰찾기 프로그램을 만들면서 느꼈던 것들을 적어봅니다. 현재 지뢰찾기 프로그램은 놀랍게도 완성되지 않았으며, 차근차근 진행중입니다. 진행 중인 프로젝트 코드는 여기서 보실 수 있습니다.

연습 욕구: 클린 아키텍처와 TDD

예전부터 하나의 작은 프로젝트를 처음부터 끝까지 설계하고 구현해보고 싶었다.
그 과정에서 각종 설계와 방법론들을 연습해보고 싶었는데, 그 중에 하나가 클린 아키텍처와 TDD였다.
특히 클린 아키텍처의 경우 실전 경험이 적었기 때문에 꼭 나만의 프로젝트로 연습해보고 싶었다.

프로젝트 선택: 지뢰찾기

작은 프로젝트로 어떤 것을 구현하면 좋을까? 고민했다. 예전부터 만들어 보고 싶었던 프로젝트들은 많았지만 적당히 작은 프로젝트로는 틱택토, 지뢰찾기, 테트리스 등이 있었다. 거기서 난이도가 너무 쉽지도 어렵지도 않아보이는 지뢰찾기 게임을 만들어보기로 결정했다. 그리고 지뢰찾기 게임의 룰에 대해서 잘 몰랐기 때문에 이 프로젝트를 통해 더 알고 싶다! 하는 생각이 들기도 했다.

학습과 구현은 chatGPT와 함께

그리고 학습 도구이자 코딩 파트너로는 chatGPT를 선택했다. 요즘은 chatGPT 없이 코딩하는 것을 상상하기 어려운 시기다. 그때그때 궁금한 것을 질문하고 일정 수준 이상의 답변을 받을 수 있는 도구가 얼마나 유용한지. 유료 버전인 chatGPT 4를 이용했고, 맥락을 유지하기 위해 하나의 chat에서 모든 질문과 답변을 이어나갔다. 하지만 여전히 어느정도 답변이 지속되면 첫 맥락은 끊기기 마련이었다.

첫 질문

지뢰찾기 게임을 html, css, js로 구현해보고 싶은데 TDD로 개발할 거야. 이 행동의 목적은 시스템 디자인, TDD를 연습하는 거야. 너와 차근차근 개발해보고 싶어. 어디서부터 시작하면 좋을까?

이렇게 물어봤지만 처음부터 몇 가지 문제가 발생했다. chatGPT는 자기 나름대로 성실하게 대답하지만, 질문자가 똑바로 답변을 검토할 수 없는 경우나 중심을 놓치는 경우가 생겼다.

  • chatGPT는 TDD 위주의 개발 방식 구현에 집중하고 클린 아키텍처에 대한 이야기는 하지 않았다.
  • 인터페이스부터 정의하지 않고 구현부터 했다.

이는 처음에는 그저 질문에 대한 답변을 받는 게 신기하고 좋아서 자칫 잘못하면 엇나가기 쉽다고 생각한다. 즉, 이를 해결하기 위해서는 정신 똑바로 차리고 내가 원하는 것이 무엇인지 잘 알아야 한다. 초심을 잃지 않아야 한다. 이는 chatGPT와 대화할 때 뿐만 아니라 평상시에도 중요한 원칙이라고 믿는다.

결정해야 하는 것들

질문과 답변을 하면서 결정해야 하는 것들이 생겼다. 내 취향에 따라 결정할 수도 있지만 해당 결정의 장단점을 알고 싶었기 때문에 차근차근 질의응답을 계속했다.

(chatGPT 답변 중) ... 어떤 테스트 프레임워크(Jest, Mocha 등)를 사용하길 원하시나요, 아니면 구체적인 구현 방법에 대해 더 논의하고 싶으신가요?

본격적으로 코드를 작성하기에 앞서, 파일 구조는 어떻게 하면 좋을까? 파일 구조를 미리 생각하는 것은 좋은 생각이 아닐까? 우선순위가 낮은가?

테스트 파일을 tests 폴더에 따로 모으기 보다는 테스트 파일을 해당 js 파일 옆에 바로 놔두는 것도 좋아보이는데 어떻게 생각해?

이 방식의 단점에는 어떤 것이 있을까?

이 프로젝트에 클린 아키텍처도 적용하고 싶어. 그러려면 테스트 폴더에 테스트 파일들을 저장해야 하는 걸까? 파일 구조를 전체적으로 어떻게 하면 좋을까?

폴더명 네이밍 컨벤션은 _를 사용하는 게 보편적이야?

이렇게 꼬리에 꼬리를 무는 질문은 그때그때 궁금증을 해결하는데 도움이 될 수도 있겠지만, 확실히 이전의 대화 맥락을 chatGPT가 잃어버릴 수 있다는 점에서 위험 부담이 있다고 느꼈다.

고민했던 것들

Board Entity

앞서 클린 아키텍처 폴더 구조를 적용하면서, Entity와 UseCase를 정의해야 하는 것을 알았다. 일종의 힌트를 얻은 셈이다. 지뢰찾기의 경우, Board라는 Entity가 존재해야 함은 확실해 보였다. 그래서 나는 Board라는 클래스 테스트를 먼저 작성해달라고 했다. 다시 생각해보면, 이는 조금 무리한 요구였다. 구체적으로 Board가 어떤 역할을 하는지 명확히 하지 않은 채 테스트 코드 작성을 chatGPT에게 맡긴 것이다.

chatGPT는 아래와 같은 테스트 코드를 작성해줬다.

import Board from '../../src/entities/board';

describe('Board 클래스 테스트', () => {
  test('보드가 정확한 크기로 초기화되어야 함', () => {
    const width = 10;
    const height = 8;
    const numberOfMines = 10;
    const board = new Board(width, height, numberOfMines);

    expect(board.board.length).toBe(height);
    expect(board.board[0].length).toBe(width);
  });

  // 추가적인 테스트 케이스를 여기에 구현...
});

나는 이런 질문을 할 수 밖에 없었다.

좋아. 그런데 네가 제공한 테스트 코드에서 board instance의 구조가 드러났는데, board.board.length 처럼, board instance에 board 프로퍼티가 있는 구조인 거지? 왜 이렇게 구조를 만들었는지 설명해줄래?

내가 먼저 Board 인터페이스를 정의할 수도 있었겠지만 이미 chatGPT의 생각을 봐버렸으니. 어떤 구조를 생각하고 있었는지 궁금해 물어보고 싶었다. 답변으로 chatGPT는 설계 결정 배경으로 "명확성과 접근성", "캡슐화와 책임 분리"를 언급했다. 맞는 말이지만 ... 어딘가 퉁 치고 넘어가는 느낌을 받았다. 지금 생각해보면 그 원인은 내가 먼저 인터페이스를 결정하지 않았기 때문이었다. Board 인터페이스를 정의하면서 Entity에 대해 충분히 고민했다면 이런 애매한 느낌은 남지 않았을 것이다.

n차원 지뢰찾기로 확장 가능 여부

Board 클래스를 구현하는 과정에서 얼핏 유튜브나 나무위키에서 보았던 n차원 지뢰찾기가 떠올랐다. 처음부터 n차원 지뢰찾기로 확장되기를 고려하고 설계하면 어떨까? 하여, Board를 초기화할 때 어떤 코드를 작성해야 하는지 chatGPT에게 물어보기도 했다.
구체적인 구현 방법에 대해 이야기를 하면서 'n차원까지 지금 굳이 확장할 필요가 있나?', 'n차원 지뢰찾기면, 2차원 지뢰찾기에서 Board였던 Entity는 어떤 이름으로 바뀌어야 하지? 개발자 으악! 머리 아파!!' 이런 생각들이 떠올라, 2차원 지뢰찾기에 한해서만 생각하기로 했다. 즉, 요구사항 또는 제품 명세를 하나 정한 것이다.

이런 사고와 의사 결정 과정이 자연스럽다면 자연스러울 수도 있지만, 별로 효율적이지는 못하다고 생각했다. 처음부터 지뢰찾기에 대한 요구사항, 명세를 정의했으면 좋았을텐데. 후회했다. 내가 생각했던 이상적인 제품 개발은 클린 아키텍처와 같이 중심부인 비즈니스 논리부터 만들고 바깥인 실제 구현까지 나아가는 방향이었다.

한참을 실제 구현과 다른 개발 환경(Test CI를 위한 Github Action 등)에 대한 이야기를 계속하다, 결국 이 질문과 그에 대한 답변이 앞으로의 개발 방향을 결정하는데 중대한 역할을 했다.

...(중략)... 그런데, Board를 바로 구현하기 전에 interface를 먼저 만드는 게 맞지 않아? 이상적인 개발 순서는 어떻게 될까? 우리는 현재 클린 아키텍처와 TDD 원칙을 지키면서 지뢰찾기 앱을 만들려고 하고 있어.