GYUD-TECH

기술명세서 작성법 본문

기술-개발

기술명세서 작성법

GYUD 2023. 10. 24. 17:51

 

우테코 프리코스를 하면서 공부한 나만의 기능명세서 작성법을 공유한다.

 

아래의 방식을 사용하면 객체의 역할을 알아보기 쉽고, 어떻게 구현할지 전체적인 그림이 그려진다는 장점이 있다. 기능명세서 작성에 정답이 있는 것은 아니기 때문에 '이렇게 작성할 수도 있구나' 정도로 참고했으면 좋겠다.

(참고로 나는 객체지향의 관점에서 기능명세서를 작성한다)

 

우테코 프리코스 백엔드 6기 1주차 과제는 아래의 링크에서 확인할 수 있다.

https://github.com/Gyu-won/java-baseball-6

 

GitHub - Gyu-won/java-baseball-6

Contribute to Gyu-won/java-baseball-6 development by creating an account on GitHub.

github.com

 


기능명세서 작성

과제에서 정확하게 기능을 명세하라는 부분이 있어서 프로그래밍을 시작하기 전에 기능명세서를 작성하였다.

아래는 내가 처음 작성한 기능명세서이다. (최종 명세서는 아니다)

기능 명세

1. 게임 플레이어는 게임을 실행한다
2. 컴퓨터가 1부터 9까지 서로 다른 임의의 수 3개를 생성한다
	-서로 다른 3개의 숫자인지 확인한다
3. 게임 플레이어는 서로 다른 3개의 숫자를 입력한다
	- 입력한 숫자 3개가 1부터 9까지의 수인지 확인한다
    - 서로 다른 3개의 숫자인지 확인한다
4. 컴퓨터는 입력한 숫자에 대한 결과를 계산한다
5. 컴퓨터는 입력한 숫자에 대한 결과를 사용자에게 알려준다
6. 결과가 맞으면 게임은 종료된다
7. 사용자는 게임을 다시 시작할지 종료할지 선택한다

 

위의 기능 명세서는 어떻게 프로그램이 진행되는지에 초점을 맞추어 작성되었다. 명세서 작성 이후 코드를 구현하려 했지만 객체를 어떻게 나누고, 메소드는 어떻게 정의할지 전혀 감이 잡히지 않았다.

 

사실 위의 기능명세서는 단순히 요구사항에 해당하는 로직을 순서대로 나열한 것이지 객체지향의 관점에서 각각의 객체들이 어떤 역할과 책임을 수행하는지 할당하지 않았다.

 


객체지향의 관점에서 작성

"객체지향의 사실과 오해" 책에서 배운 내용을 떠올리면서 처음부터 객체의 관점에서 기능명세서를 작성하였다.

 

1. 어떤 객체가 필요할지 떠올린다

중학교때 친구와 숫자야구를 한 경험을 떠올리며 어떤식으로 게임이 진행되었는지 생각해보았다.

 

플레이어가 A와 B가 있으면 A는 자신이 추측한 값을 B에게 전달하고, B는 자신이 생성한 정답과 A의 추측값을 비교하여 추측의 결과를 알려준다.

그래서 우선 A라는 Player 객체와 B라는 Computer 객체가 필요하다고 생각하였다. 그리고 과제에서 제시한 게임 재시작 여부를 처리하고 순서에 맞게 객체의 메소드를 실행하기 위하여 GameController 객체를 추가하였다.

 

2. 프로그램의 목적을 수행하기 위해서 객체들이 서로 어떤 명령을 내려야 할지 정한다

이 객체들이 서로 어떻게 협력하는지 생각하여 아래의 그림을 그려보았다.

 

위의 내용을 바탕으로 아래와 같은 기능 명세서를 작성하였다.

기능 명세

GameController
- 게임 운영을 관리한다


Player
- Player는 1부터 9까지 서로 다른 수로 이루어진 3자리 수를 입력한다.
   - 입력된 수가 null 이 아닌지 검사한다.
   - 입력된 수의 길이가 3인지 검사한다.
   - 입력된 수의 모든 자리가 1~9 사이인지 검사한다.
   - 입력된 수의 모든 자리가 서로 다른 3개의 숫자인지 검사한다.

- Player는 게임을 다시 시작할지 종료할지에 대한 값을 입력한다.
  - 1 이나 2가 아니면 IllegalArgumentException을 발생시키고 애플리케이션을 종료한다.
  - 1 이면 게임을 다시 시작한다.
  - 2 이면 게임을 종료한다.


Computer
- Computer는 1에서 9까지의 서로 다른 임의의 수 3개를 선택한다
  - 랜덤으로 1에서 9까지의 수 중 하나를 생성
  - 해당 값이 이미 선택한 값인지 확인
- Computer는 Player가 입력한 숫자에 대한 결과를 출력한다
  - 볼과 스트라이크의 합을 계산한다
  - 스트라이크를 계산한다
  - 볼을 계산한다

각 객체에서 수행해야하는 책임을 정의하고, 책임을 수행하기 위한 서브 기능들을 정의하였다. 서브기능은 한가지의 역할만 수행하도록 하였고, 한개의 서브기능은 한개의 메소드로 옮겨졌다.

 


다른 기능명세서 참고

코드를 모두 구현하고 리팩토링을 하기위해서 자료를 찾던 중 우아한테크 세미나에서 예시로 설명하신 기능명세서를 보게되었다. 

https://www.youtube.com/watch?v=bIeqAlmNRrA&list=WL&index=9&t=2647s

 

위 영상에 나오는 기능명세서의 특징은 []를 사용하여 어떤 객체가 책임을 수행하는지 또는 예외처리 기능인지를 표시하는 것이다.

 

 

이 방식은 마치 커밋 메시지를 작성하는 것과 같다는 생각을 하였다. 세부기능 단위로 커밋을 해야하는데, 어쩌면 기능 명세서에서 작성하는 부분이 커밋의 한 단위를 작성하는 것이라는 생각이 들었다. 그래서 커밋메시지의 형식에 맞게 기능명세서를 작성하고자 노력하였다.

기능 명세

Player
1. 올바른 질의 숫자를 생성하는 기능
    - [기능] 사용자의 입력을 받는 기능
    - [예외처리] 입력된 수가 null 인 경우
    - [예외처리] 입력된 수의 길이가 3이 아닌 경우
    - [예외처리] 입력된 수가 1~9 외의 다른 수를 포함하는 경우
    - [예외처리] 입력된 수가 중복되는 수를 가지는 경우

2. 게임을 다시 시작할지 종료할지에 대한 값을 입력하는 기능
    - [기능] 사용자의 입력을 받는 기능
    - [예외처리] 입력된 수가 null 인 경우
    - [예외처리] 입력된 수가 1 또는 2가 아닌 경우
    - [기능] 1인 경우 재시작, 2인 경우 종료하는 기능


Computer
1. 올바른 정답 숫자를 생성하는 기능
    - [기능] 1~9까지의 수를 무작위로 생성하는 기능
    - [기능] 선택한 값이 중복된 값인지 확인하는 기능

2. Player가 입력한 숫자에 대한 결과를 생성하는 기능
    - [기능] 스트라이크를 계산하는 기능
    - [기능] 볼을 계산하는 기능


GameController
1. 게임을 시작하는 기능
    - [기능] 새로운 라운드를 시작하는 기능
    - [기능] 라운드에서 한개의 질의를 받고 평가하는 기능
    - [기능] 질의결과를 통해 게임의 라운드의 진행여부를 판단하는 기능


## 기능명세서 참고사항
출력 결과 예시에 따르면, 게임 시작 메시지는 게임 재시작 여부와 상관없이 한번만 출력되기 때문에 게임이 전체를 뜻하는건지, 숫자야구 한판을 뜻하는건지 모호한 것 같습니다.
따라서 라운드 개념을 추가하여, 게임은 전체를 사이클을 의미하고, 라운드가 숫자야구 한판을 의미하도록 기능을 명세하였습니다

 

하지만 작성후 생각해보니 중복되는 내용이 너무 많고, 기능과 예외처리를 구별하는 것은 어차피 모두 feat 커밋 종류로 작성되기 때문에 불필요하다고 생각되었다. 이런 문제점을 반영하여 최종 기능명세서를 작성하였다.

 


클래스 분리

리팩토링 과정에서는 객체의 역할에 초점을 맞추어, 한가지 이사의 역할을 하는 객체를 분리하였다.

 

숫자야구 게임을 떠올려보면, Player A가 추측값을 생성하면 이 추측값을 Player B가 받아서 미리 적어둔 정답과 비교하는 과정을 거친 후 결과를 생성한다. 컴퓨터와 게임을 하는 Player는 단순히 입력만 하고 입력값에 대한 유효성 검사는 Player가 아닌 다른 객체가 수행하는 것이 맞다고 생각했다. 

 

따라서 Guess, Answer, Result, RestartFlag 객체를 추가하였고 Computer 객체가 GameController의 역할을 수행하기 때문에 GameController 객체는 삭제하였다.

 


정리

이러한 과정을 거치면서 확립한 나만의 기능 명세서 작성법은 아래와 같다.

 

1. 어떤 기능들이 필요한지 정리한다

1. 컴퓨터가 1부터 9까지의 서로 다른 임의의 수 3개를 선택한다
    - 1부터 9까지의 숫자들 중 랜덤으로 숫자를 선택한다
    - 선택한 숫자가 선택된 숫자 목록에 포함되는지 확인한다

2. 플레이어는 서로 다른 3개의 숫자를 입력한다
    - 값을 입력한다
    - 입력한 값이 null인지 확인한다
    - 입력한 값이 3자리 수 인지 확인한다
    - 입력한 값이 각각 1에서 9의 범위에 포함되는지 확인한다
    - 입력한 값이 서로 다른 숫자인지 확인한다
    
3. 컴퓨터는 입력한 숫자에 대한 결과를 계산한다
    - strike를 계산한다
    - ball을 계산한다
    - 결과를 문자열로 반환한다

4. 컴퓨터는 게임의 진행 여부를 판단한다
    - 결과가 3스트라이크인지 판단한다

5. 게임이 종료되었다면 게임 재시작 여부를 확인한다
    - 플레이어는 게임 재시작 여부를 입력한다
    - 입력한 값이 null이 아닌지 확인한다
    - 입력한 값이 1 또는 2 외의 값인지 확인한다
    - 입력한 값이 1인지 2인지 판단한다
    - 컴퓨터는 게임을 재시작 하거나 종료한다

 

 

2. 필요한 객체를 떠올리고 객체에 책임을 할당한다

우선 Computer와 Player 객체가 필요하다.

 

그리고 정답값, 추측값, 재시작 입력값이 올바른지에 대한 여부의 판단은 컴퓨터와 플레이어가 하지 않고 Answer, Guess, RestartFlag 라는 객체를 두어 따로 처리하도록 한다.

 

결과 값을 만드는 과정도 계산 과정이 많기 때문에 별도의 Result 객체로 분리한다.

 

추가로 객체에 책임을 표현하기 위해 [] 안에는 책임을 수행할 객체를 명시하였다.

기능 명세

1. 1부터 9까지의 서로 다른 임의의 수 3개를 선택한다
    - [Answer] 1부터 9까지의 숫자들 중 랜덤으로 숫자를 선택한다
    - [Answer] 선택한 숫자가 선택된 숫자 목록에 포함되는지 확인한다

2. 서로 다른 3개의 숫자를 입력한다
    - [Player] 값을 입력한다
    - [Guess] 입력한 값이 null인지 확인한다
    - [Guess] 입력한 값이 3자리 수 인지 확인한다
    - [Guess] 입력한 값이 각각 1에서 9의 범위에 포함되는지 확인한다
    - [Guess] 입력한 값이 서로 다른 숫자인지 확인한다
    
3. 입력한 숫자에 대한 결과를 계산한다
    - [Result] strike를 계산한다
    - [Result] ball을 계산한다
    - [Result] 결과를 문자열로 반환한다

4. 게임의 진행 여부를 판단한다
    - [Computer] 결과가 3스트라이크인지 판단한다

5. 게임이 종료되었다면 게임 재시작 여부를 확인한다
    - [Player] 게임 재시작 여부를 입력한다
    - [RestartFlag] 입력한 값이 null이 아닌지 확인한다
    - [RestartFlag] 입력한 값이 1 또는 2 외의 값인지 확인한다
    - [RestartFlag] 입력한 값이 1인지 2인지 판단한다
    - [Computer] 컴퓨터는 게임을 재시작 하거나 종료한다

 

위 기능명세서는 가시성도 뛰어나고 객체가 수행하는 역할전체적인 프로세스의 과정도 알 수 있다. 

다음 2주차 과제부터 위의 방식을 적용하여 기능명세서를 작성할 것이다.

 


마무리

완벽한 설계는 없다

우테코 커뮤니티와 구글에서 다양한 자료들을 찾아보면서 기능명세서 작성법에도 정답이 없다는 것을 알았다. 

코드를 구현하기 전에 TDD나 기능명세서를 완벽하게 하면 좋겠지만, 설계자가 모든 상황과 버그를 예상하여 처음부터 이를 대비하는 것은 불가능에 가깝다고 생각한다.

 

하지만 완벽하진 않더라도, 전반적인 프로그램의 설계 프로그램을 잘 설명하는 목적은 달성해야 한다고 생각한다. 기능을 구현하면서 수정을 할 수 있지만, 전체적인 틀을 잡고 다른사람에게 내 프로그램을 명확하게 설명하는 정도는 되어야 한다는 뜻이다. 

 

앞으로

위에서 확립한 기능 명세서로 새로운 과제를 풀어보면서 얼마나 바뀔 부분이 있는지 체크하고 계속 수정해 나갈 것이다. 특히 객체의 역할가시성에 초점을 맞추어서 나만의 기능 명세서 작성을 연습할 것이고, 이를 통해 프리코스가 끝날때 쯤에는 개발 전에 전반적인 설계를 할 수 있는 개발자로 성장해 있을 것이다.

'기술-개발' 카테고리의 다른 글

단위 테스트를 작성하며 객체지향 적용하기  (0) 2025.01.27
Stream 적용기  (0) 2023.10.30
좋은 커밋 메시지 작성법  (1) 2023.10.25
클린코드를 연습하는 이유  (1) 2023.10.24