[Clone Coding] Make Wordle using Phoenix LiveView: Requirements Analysis & Project Setup
Phoenix LiveView로 wordle 만들어보기 2번째: 요구사항 분석 및 프로젝트 초기 세팅
이전 시리즈
Requirements
Functional Requirements
- 사용자가 5개 알파벳으로된 word를 맞추면 승리하는 게임
- 6번의 시도 안에 맞춰야 한다.
- 단어는 알파벳 26자로만 이루어져 있다.
- 단어는 서버가 가진 별도의 사전에서 랜덤으로 뽑힌 단어이며, 사용자는 서버 사전에 존재하는 단어로만 입력을 시도할 수 있다.
- 알파벳은 중복해서 쓰일 수 있다.
- 사용한 알파벳에 대해 표식이 붙는데, 위치까지 일치하는 알파벳은 초록색, 위치는 맞지 않지만 정답에 존재하는 알파벳은 노란색, 존재하지 않는 알파벳은 회색으로 표시된다.
- 사용한 알파벳중 회색표시된 알파벳은 다시 쓰일 수 없다.
Non-FR
- 사용자마다 각각 게임 서버 프로세스를 띄우도록 할 예정
- 게임 서버 프로세스, 단어 모음집, 웹 서버를 적절히 분리한다.
프로젝트 세팅
PreRequisite
-
elixir/OTP version: 13.3/24.1 (installed by
asdf
package manager) -
phx_new
: installed bymix archive.install hex phx_new
-
dependencies version
-
:phoenix
: 1.6.6 -
:phoenix_live_view
: 0.17.5 -
:tailwind
: 0.1 -
:esbuild
: 0.3
-
Project Structure
프로젝트를 생성하기에 앞서 어떤 구조로 만들것인지 먼저 생각해보고, 대략적인 구조를 잡아본다. 생각해볼 점은 비즈니스 로직인 게임 모듈과 게임에서 필요한 단어 모음집을 불러오는 저장소 모듈, 웹 인터페이스 모듈을 어떻게 적절히 분리해볼까에 대한 부분이다.
Dave Thomas의 ‘Elixir for Programmers, 2nd Edition’ 강의 에서 hangman 게임을 만들때 소개한 프로젝트 구조가 마음에 들어서, 그와 비슷하게 구조를 잡아보았다.
우선 단어 저장소 모듈을 Dictionary
, 게임의 인터페이스 모듈은 MyWordle
, 웹 모듈은 MyWordleWeb
이렇게 최상위 네임스페이스와 최상위 인터페이스 모듈들을 정의한다.
웹 인터페이스에서 유저가 게임을 실행하면 게임 인터페이스를 호출하고, 게임은 내부에서 자체 프로세스에서 실행되며, Dictionary에서 단어를 불러온다.
classDiagram
Dictionary <|-- Runtime
MyWordleWeb <|-- Runtime
MyWordleWeb --|> MyWordle
MyWordle --|> GameServer
Game <|-- GameServer
GameServer <|-- Runtime
MyWordle_Dictionary <|-- Game
Dictionary <|-- MyWordle_Dictionary
class Runtime~MyWordle~{
Application
GameSupervisor
}
class MyWordle{
}
class Game
class Dictionary{
Dictionary Server
}
class MyWordleWeb{
Web Server
}
최종적인 프로젝트 폴더 구조는 다음과 같이 만들어졌다.
my_wordle
├── lib
│ ├── dictionary
│ │ ├── server.ex
│ │ └── word_list.ex
│ ├── dictionary.ex
│ ├── my_wordle
│ │ ├── dictionary.ex
│ │ ├── game_type.ex
│ │ ├── impl
│ │ │ └── game.ex
│ │ ├── runtime
│ │ │ ├── application.ex
│ │ │ ├── game_supervisor.ex
│ │ │ └── server.ex
│ │ └── type.ex
│ ├── my_wordle.ex
│ ├── my_wordle_web
│ │ ├── controllers
│ │ │ └── page_controller.ex
│ │ ├── endpoint.ex
│ │ ├── gettext.ex
│ │ ├── live
│ │ │ ├── game
│ │ │ ├── game.ex
│ │ │ ├── game.html.heex
│ │ │ └── live_helpers.ex
│ │ ├── router.ex
│ │ ├── telemetry.ex
│ │ ├── templates
│ │ │ ├── layout
│ │ │ └── page
│ │ └── views
│ │ ├── error_helpers.ex
│ │ ├── error_view.ex
│ │ ├── layout_view.ex
│ │ └── page_view.ex
│ └── my_wordle_web.ex
├── mix.exs
├── mix.lock
...
강의에서처럼 아예 3개의 app을 결합하는 식으로 하면 더 경계가 분명하겠지만, 일단 한 프로젝트 내에서 최대한 처리해보고자 전부 lib
아래로 집어넣었다.
그럼 이제 프로젝트를 생성해볼 차례다.
Create New Project using mix phx.new
먼저 프로젝트를 생성한다. Phoenix LiveView 프로젝트이기 때문에 phx_new
mix task를 활용한다.
원래 --live
옵션을 넣어야 phoenix_live_view 의존성이 들어갔었는데, 이젠 --no-live
옵션을 주지 않으면 default로 live_view 가 들어가는것 같다.
이 프로젝트에서 Database는 현재 고려대상이 아니므로,
--no-ecto
옵션을 붙여서 Ecto 관련 의존성을 제거한다. 마찬가지로 mail 관련 기능 역시--no-mailer
옵션으로 제거한다.
mix phx.new my_wordle --no-ecto --no-mailer --install
그럼 이제 다음처럼 프로젝트 생성 및 의존성 불러오기가 진행된다.
* creating my_wordle/config/config.exs
...
* creating my_wordle/lib/my_wordle/application.ex
* creating my_wordle/lib/my_wordle.ex
...
* creating my_wordle/mix.exs
* creating my_wordle/README.md
* creating my_wordle/.formatter.exs
* creating my_wordle/.gitignore
...
* creating my_wordle/assets/js/app.js
...
* running mix deps.get
* running mix deps.compile
We are almost there! The following steps are missing:
$ cd my_wordle
Start your Phoenix app with:
$ mix phx.server
You can also run your app inside IEx (Interactive Elixir) as:
$ iex -S mix phx.server
Setup TailwindCSS for Phoenix
CSS 프레임워크인 tailwind를 세팅해보자. tailwind 공식문서 가이드에 Phoenix 프로젝트에 설치하는법이 친절하게 나와있으므로 보고 따라해보면 어렵지 않다.
Note
위 가이드를 보면 중간에
mix tailwin.install
하는 step이 있는데, 직접 해본 바로는 저 명령어 이후 step은 여기서 자동으로 이뤄지는것 같다.
page 템플릿 밑의 index.html.heex
파일을 수정해서 tailwind CSS가 잘 동작하는지 확인해본다.
# lib/my_wordle_web/templates/page/index.html.heex
<section class="w-full text-center">
<h1 class="text-3xl font-bold underline">
<%= gettext "Welcome to %{name}!", name: "Phoenix" %>
</h1>
</section>
mix phx.server
로 실행해보면 일단 다음과 같은 페이지를 볼 수 있다.
다음 포스트에서는 요구사항을 만족하는 Game 모듈을 만들어보도록 하겠다.