각종 후기/우아한테크코스

[우아한 테크코스 3기] LEVEL 1 회고 (56일차)

제이온 (Jayon) 2021. 3. 29.

안녕하세요? 제이온입니다.

 

오늘은 투두리스트 2단계 미션에서 배운 ajax 지식을 이용하여 체스 미션 4단계를 진행해 보았습니다.

 

 

데일리 미팅

오늘의 데일리 미팅 진행자는 아론이었습니다. 그동안 데일리 미팅 콘텐츠로 게임만 했기 때문에 이번에는 '우테코 레벨1 동안 가장 좋았던 경험과 가장 힘들었던 경험'에 대해 이야기하기로 했습니다. 대부분 가장 좋았던 경험으로는 현재 워니조와 함께한 것을 꼽았고, 힘들었던 경험으로는 저번 주를 꼽았습니다.

 

저도 크루들과 함께하는 순간이 모두 행복하지만, 가장 좋았던 경험은 역시 나봄과 처음으로 페어프로그래밍을 하여 결과물을 냈을 때였습니다. 프리코스를 진행하면서, 한 번도 제 코드에 만족한 적이 없었고 항상 어딘가 불만족스러웠습니다. 반복된 코드, 무계획적인 개발 등 여러모로 흡족스럽지 않았습니다.

 

하지만, 처음으로 페어프로그래밍을 진행하면서 잘하는 크루에게 지식을 배우고 함께 하나의 문제를 해결해 나가는 과정이 즐거웠습니다. 그리고 그렇게 노력해서 나온 결과물도 스스로 만족스러웠습니다. 물론 지금보면 또 고칠 부분이 보이지만, 그 당시에는 처음으로 여러 가지 OOP 개념을 적용한 것 자체가 고무적이었다고 생각합니다.

 

가장 힘들었던 경험은 저 역시 지금 '현재'입니다. 여태까지는 나름 커리를 타면서 미션을 한두개씩 던져줬었는데, 이번에는 대량의 미션이 쏟아졌습니다. 체스 콘솔 버전과 체스 웹 버전 미션, 투두 리스트 1단계와 2단계, SQL 퀴즈, 글쓰기 미션이 바로 그것들이죠.

 

다행히 아론과 함께 힘내서 체스 콘솔 버전은 미리 잘 끝내놔서 merge가 금방 되었습니다. 하지만, 나머지 JS 쪽은 제가 거의 문외환이어서 정말로 고생했습니다. 다행히 그 속에서 제리와 아론 및 다양한 크루의 도움을 받아서 미션을 수행하고 있어서 기분이 좋습니다. 2단계를 대비하기 위한 채찍이라고 생각하고, 이제는 겸허히 받아들이려고 합니다.

 

 

체스 미션 4단계

이전 포스팅에서, 저는 체스판을 띄우고 체스말을 선택하여 좌표를 얻어오는 작업까지 했다고 말씀드렸습니다. 하지만, 서버와 통신하는 방법을 몰랐기에 주말동안 투두 리스트 2단계 미션에 올인하였습니다. 오늘 오전에는 투두 리스트 2단계 미션 코드를 간단히 수정해서 pr을 날렸고, fetch api를 사용하여 ajax 통신하는 법을 배울 수 있었습니다.

 

이를 바탕으로, 오늘은 체스말을 이동하는 것을 목표로 개발을 진행했습니다.

 

 

async function onSelectPiece(event) {
    const nowClickedPiece = event.target.closest("td");
    const pastClickedPiece = decideClickedPiece();

    if (pastClickedPiece === "") {
        if (nowClickedPiece.childElementCount === 0) {
            alert("빈 공간은 선택할 수 없습니다!");
            return;
        }
        nowClickedPiece.classList.toggle("clicked");
        return;
    }
    clearClicked();
    await move(pastClickedPiece.id, nowClickedPiece.id);
    await checkEndGame();
}

function decideClickedPiece() {
    const tds = document.getElementsByTagName("td");
    for (let i = 0; i < tds.length; i++) {
        if (tds[i].classList.contains("clicked")) {
            return tds[i];
        }
    }
    return "";
}

function clearClicked() {
    const tds = document.getElementsByTagName("td");
    for (let i = 0; i < tds.length; i++) {
        if (tds[i].classList.contains("clicked")) {
            tds[i].classList.remove("clicked");
        }
    }
}

 

 

현재까지 선택한 위치값이 2개라면, move() 함수를 수행하는 방식입니다. 역시 중요한 것은 move()겠죠?

 

 

async function move(sourcePosition, targetPosition) {
    const data = {
        source: sourcePosition,
        target: targetPosition
    };

    const option = {
        method: 'POST',
        header: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(data)
    };

    await fetch("/move", option)
    .then(res => {
        return res.json();
    })
    .then(obj => {
        if (obj.code === "401") {
            alert(obj.message);
            return;
        }
        settingImg(sourcePosition, targetPosition);
        changeTurnText();
    });
}

function settingImg(sourcePosition, targetPosition) {
    const source = document.getElementById(sourcePosition);
    const target = document.getElementById(targetPosition);
    const piece = source.getElementsByTagName("img")[0];
    if (target.getElementsByTagName("img")[0]) {
        target.getElementsByTagName("img")[0].remove();
    }
    target.appendChild(piece);
}

function changeTurnText() {
    if (document.getElementById("user-turn").innerText === 'White') {
        document.getElementById("user-turn").innerText = 'Black';
        return;
    }
    document.getElementById("user-turn").innerText = 'White';
}

 

 

체스말이 움직이면 특정 태그들 및 서버의 체스 보드에 변화가 일어나므로 POST 방식을 채택했습니다. 그리고 전형적인 fetch 방식으로 서버에 통신을 보냈습니다. 그리고 서버에게서 401번 에러 코드를 받았다면, 에러 메시지를 창으로 보여주고 그렇지 않다면 정상 작동이므로 HTML 요소를 조작하면 됩니다. 중요한 것은 서버에서 프론트로 JSON 파싱하는 방법을 몰라서 애를 좀 먹었습니다.

 

 

        post("/move", (req, res) -> {
            final String requests = req.body();
            final MoveRequest moveRequest = GSON.fromJson(requests, MoveRequest.class);

            try {
                chessGame.move(getPositionByCommands(moveRequest.source().split("")),
                    getPositionByCommands(moveRequest.target().split("")));

                if (chessGame.isKingDead()) {
                    chessGame.changeGameOver();
                }
                chessGame.nextTurn();
                return new Response("200", "성공");
            } catch (UnsupportedOperationException | IllegalArgumentException e) {
                return new Response("401", e.getMessage());
            }
        }, JSON_TRANSFORMER);

 

 

위에서 post의 3번째 인자로 JSON_TRANSFORMER을 넘겨주면 되는데 이 객체는 아래와 같이 정의됩니다.

 

 

import com.google.gson.Gson;
import spark.ResponseTransformer;

public class JsonTransformer implements ResponseTransformer {

    private final Gson gson = new Gson();

    @Override
    public String render(Object model) {
        return gson.toJson(model);
    }

}

 

 

아주 정확한 작동 원리는 모르겠으나, spark java 공식 문서에서는 위 클래스를 사용하여 프론트에 JSON 파싱하여 데이터를 보낼 것을 추천하고 있습니다.

 

 

이 과정까지가 시간이 굉장히 오래 걸렸고, 나머지는 쉬웠습니다. 애초에 핵심 로직은 콘솔에서 개발할 때 이미 다 짜놨기 때문이죠. 결과를 출력하는 창은 location.replace() 함수를 사용하여 '/result'로 리다이렉션한 후, result.html을 작성하였습니다. 아직 게임을 강제 종료하는 기능은 없으므로 점수 출력하는 기능은 구현하지 않았습니다.

 

여기까지 얼추 체스 4단계는 완성하였습니다.

 

 

 

 

이런식으로 체스말을 이동시킬 수 있습니다. 그리고 상대편 킹이 잡히면 게임은 종료가 됩니다.

 

 

정리

새로운 개념을 짧은 시간 안에 배워서 본래 미션에 적용하는 것이 참 어려운 것 같습니다. 다행히도 주변에는 프론트엔드 공부를 하였던 학교 동기도 있고, 잘하는 크루도 있어서 많은 도움을 받았습니다. 덕분에 프로미스나 async, await에 대한 부분을 조금이나마 이해할 수 있었습니다.

 

내일은 SQL과 DB를 빠르게 숙지해서 체스 5단계 필수 요구 사항까지만 하고 PR을 날릴 수 있으면 좋겠습니다.

댓글

추천 글