작년 5월에 출시한 첫 개인 프로젝트 <방탈출고사>의 후기를 한 번 자유롭게 써보기 위해 오랜만에 포스트를 작성합니다. 후기 작성이 이렇게 늦어진 이유는, '방탈출고사'를 출시하자마자 기말고사 + 계절학기가 세트로 절 기다리고 있었기 때문이었습니다. 그리고 계절학기 수업의 중간고사가 끝난 지금, 뒤늦게 마음을 잡고 글을 써봅니다.
후기는 2개의 Part로 나누어 작성하려 합니다. 우선 Part 1에서는 <방탈출고사>라는 앱의 기획 과정과, 앱에 필요한 기능을 구현하면서 겪었던 난관 및 극복 과정 등 '기술적인 측면'에 초점을 맞춰 작성할 예정이고, Part 2에선 <방탈출고사> 게임 콘텐츠를 개발하며 겪었던 고민이나 생각 등과 같은 '콘텐츠적 측면'에서 후기를 작성해보려 합니다.
이 포스트에서는 우선 기술적인 측면에 대해 후기를 적어보고, 콘텐츠적 측면에 관해선 다음 포스트에 다뤄보도록 하겠습니다.
(🎮 방탈출고사는 해당 링크를 통해 직접 플레이해보실 수 있습니다.)
📋 기획 배경 및 아이디어 구체화 과정
먼저 이 프로젝트를 처음 기획하게 된 배경과, 제 아이디어를 구체화하여 개발을 시작하기까지의 일련의 과정을 적어보고자 합니다.
1. 주어진 상황을 고려하여 프로젝트 규모 및 기술 스택 정하기
'방탈출고사'는 SWYG에서 한 달 동안 진행했던 SWYDY 스터티를 통해 제가 혼자서 개발한 개인 프로젝트입니다. 이 때 실제 개발에 주어진 시간은 대략 2주 정도였는데, 제가 속해있던 IT 동아리(멋사)에서 (이 프로젝트와는 별개로) 팀 프로젝트를 함께 진행하고 있었던 지라, 실제 프로젝트에 주어진 시간은 이보다 훨씬 더 부족한 상황이었습니다. 그래서 프로젝트의 규모를 최대한 작고 단순하게 잡아야 했습니다.
또한 새로운 기술 스택을 공부해서 프로젝트에 적용해 볼 여유는 없다고 판단하여, 적어도 제가 최소한 한 번 이상 사용해본 기술만을 사용하기로 결정했습니다. 따라서 기술 스택은 프론트엔드의 기본 3대장에 리엑트와 CSS-in-JS으로 정했고, 아쉽게도 TypeScript나 Redux, NextJS 등 제가 사용해보고 싶었던 기술들은 프로젝트를 마치고 난 이후에 리펙토링을 진행하면서 하나씩 적용해보기로 했습니다.
2. 무엇을 만들 것인지 정하기
그 다음으로 어떤 앱을 만들지에 대해 고민했습니다. 현재 주어진 시간과 저의 개발 실력을 고려했을 때 복잡한 로직이 많이 들어간 앱은 기간 내에 구현하기 어렵다고 판단하여, 간단하게 구현할 수 있는 소재를 개발 대상으로 삼기로 했습니다. 그렇게 해서 정한 소재가 '방탈출 관련 미니 게임' 콘텐츠였습니다.
해당 컨텐츠는 제가 가장 만들어보고 싶은 앱임과 동시에 복잡한 동작을 수행하는 기능이 크게 필요로 하지 않았다는 점에세 선정하게 되었습니다. (좀 더 자세한 이유는 다음 Part에서 다루겠습니다)
3. 기획을 구체화하여 구현할 목표 기능 설정하기
무엇을 만들지 정하고 나니 이제 좀 더 구체적인 기획이 필요했습니다. 2주라는 짧은 시간 내에 원하는 앱을 구현해내려면 중간에 갈아엎는 일이 없어야 했기에 그만큼 기획을 잘 해두어야 한다고 생각했습니다. 그래서 이 부분에 특히 신경을 많이 썼습니다.
우선 '방탈출 관련 미니 게임'이라는 소재로 어떤 작품을 만들지에 대한 고민이 필요했습니다. 이 때 제가 생각한 아이디어는 두 가지가 있었는데, 우선 첫 번째로 마치 시험처럼 한 번에 제공된 방탈출 문제들을 시간 내에 아는 만큼 풀어 답안지는 제출하는 게임 방식이 있었고, 둘째로는 실제 방탈출이나 미궁 게임처럼 주어진 방탈출 문제를 순서대로 차근차근 풀어나가는 게임 방식이 있었습니다.
고민 끝에 저는 전자의 방식을 택하였습니다.
무엇을 만들지를 결정하고 나니, 이제 앱에 필요한 페이지와 필요한 기능이 점점 명확해지기 시작했습니다.
4. 설정한 목표를 바탕으로 본격적인 개발 시작하기
이제부터 본격적으로 개발을 시작하였습니다. 방탈출 문제를 제작해서 이미지 파일로 만드는 과정, 앱의 전반적인 분위기를 설정하여 그에 맞는 배경 이미지와 폰트를 고르는 일 등 아직 기획 단계에서 좀 더 해야할 일들이 있긴 했지만, 이 부분은 개발과 동시에 진행하는 것이 시간 효율적 측면에서 더 낫겠다는 생각이 들었습니다.
프로젝트를 초기 세팅할 땐 CRA를 통해 진행했습니다. 물론 직접 필요한 패키지를 일일이 설치하는 방법도 있었습니다만, 여기에 시간을 들이는 것보단 개발 및 기획에 시간을 들이는 것이 더 낫다고 생각하여, 유용한 패키지들을 한 번에 설치할 수 있는 CRA로 초기 세팅 시간을 절약하고자 했습니다.
🖥️ 구현 페이지 및 각 페이지별 기능 소개
그 다음으로 앱에 어떠한 페이지와 기능을 구현할 지, 해당 기능을 구현하기 위해 어떠한 고민을 하며 코드를 작성했는지에 대해 간단히 적어보겠습니다.
우선 어떤 페이지가 필요할 지부터 생각해 본 다음, 해당 페이지에 넣을 기능을 하나씩 정리해보았습니다. 그리고 기능 구현은 각 페이지에 꼭 들어가야 할 필수 기능부터 우선순위를 정해 개발하고, 여유가 되면 부가 기능을 하나씩 더 추가하는 방식으로 진행했습니다.
■ 시작 페이지
앱을 처음 실행했을 때 등장하는 첫 페이지로 해당 앱(게임)의 첫 인상을 결정해주는 페이지입니다. 최대한 단순하고 깔끔하게 보이도록 개발했습니다.
< 구현 기능 >
- 다음 페이지 이동 버튼 구현
■ 시험 난이도 선택 페이지
게임의 난이도를 선택하는 페이지입니다. 총 2가지 종류가 있습니다. 원하는 시험 종류를 선택하면 다음 페이지로 넘어갑니다.
< 구현 기능 >
- 테스트 선택 기능
■ 게임 시작 전 주의사항 안내 (Before Start) 페이지
게임 시작 전 주의해야 할 사항을 안내해주는 페이지입니다. 닉네임을 입력하고 시작하기를 누르면 게임(시험)이 시작됩니다.
< 구현 기능 >
- 닉네임 저장해서 게임 페이지를 거처 게임 결과 안내 페이지까지 전달하는 기능
- 다음 페이지로 이동하여 시험을 자동으로 시작해주는 버튼 구현
■ 게임 페이지
게임이 진행되는 페이지입니다. 가장 많은 기능이 필요로 하는 페이지기이기도 합니다.
< 구현 기능 >
- 테스트 타이머 기능
- 시험 중 문제 이동 기능
- 답안지 작성 기능 (답안지에 Toggle 기능을 달아 열고 닫을 수 있도록 함)
- 시험 종료 시 답안지 작성 내용을 시험 결과 안내 페이지로 전달하는 기능
- 시험 종료 버튼 클릭 시, 재확인 경고 문구를 (모달창으로) 표시하는 기능
- 시험 도중 새로고침, 뒤로 가기 방지 기능
■ 게임 결과 안내 페이지
게임의 결과를 안내해주는 페이지입니다. 게임 페이지 다음으로 구현할 기능이 많은 페이지입니다.
< 구현 기능 >
- 이전 페이지에서 받은 닉네임, 답안지 데이터를 통해 답안지를 자동으로 채점하는 기능
- 채점 결과를 실제 정답과 함께 사용자에게 보여주는 기능
- 최종 점수와 등급을 계산하여 화면에 표시해주는 기능
- 링크가 연결된 버튼 및 문구를 통해 시험 이후의 활동을 안내해주는 기능
📦 구현한 대표 기능 소개 (with Code)
■ 테스트 선택 기능
- 시험 난이도 선택 페이지에서 '기초 역량 테스트'와 '고수 테스트' 둘 중 하나를 선택해 응시할 수 있음
각각의 옵션에 해당하는 버튼을 통해 원하는 테스트를 고를 수 있도록 구현했습니다. 버튼을 구현한 코드는 React의 JSX 문법으로 작성한 후 CSS-in-JS로 스티일링을 했습니다. 구현 시 어떤 복잡한 로직이 필요하진 않았으므로, 해당 기능을 구현한 코드를 포스트에 따로 첨부하지 않았습니다.
■ 테스트 타이머 기능
- 게임 페이지에 접속하자마자 테스트가 시작되어 타이머 카운트다운 시작
- 타이머의 남은 시간이 0이 될 시, 테스트를 종료하고 답안지를 자동으로 제출
// 타이머 설정 코드 - useState Hook
const [timeRemain, setTimeRemain] = useState(1800);
// 타이머 설정 코드 - 기능 수행 Hook
useEffect(() => {
const x = setInterval(() => {
setTimeRemain(timeRemain => timeRemain - 1);
}, 1000);
if (timeRemain < 0) {
moveToResultPage();
}
return () => clearInterval(x);
}, [timeRemain]);
return (
<MainBody>
<HeaderArea>
<Timer>
// 남은 시간 '분' 계산 : 남은 시간 '초' 계산
{String(parseInt(timeRemain / 60)).padStart(2, '0')}:
{String(timeRemain % 60).padStart(2, '0')}
</Timer>
</HeaderArea>
...
</MainBody>
...
);
💡해당 코드 작성 시 마주했던 문제와 해결 방법
- 제한 시간이 1초씩 감소될 때마다 남은 시간을 화면에 바로바로 반영하려면 어떻게 해야할 지 고민이었는데, 여기서 useEffect를 활용하여 timeRemain값이 업데이트(1씩 감소)될 때마다 화면이 재랜더링 되도록 코드를 작성했더니, 해당 문제를 해결할 수 있었습니다. 덕분에 막연했던 useEffect의 문법과 활용법에 좀 더 친숙해질 수 있었습니다.
- 디지털 시계의 표시되는 시간이 한 자리수가 될 때, 예를 들어 12:09시를 표시하려 할 때, 12:9가 아닌 12:09으로 바르게 표시되도록 하려면 padStart라는 매서드를 사용하면 된다는 것을 알았습니다.
■ 시험 중 문제 이동 기능
- 버튼을 통해 문제 번호를 앞 혹은 뒤로 이동할 수 있도록 구현
- 문제 번호 1번과 20번을 연결하여, (다음 문제로 이동하는 버튼으로) 1번에서 20번, (이전 문제로 이동하는 버튼으로) 20번에서 1번으로도 문제 이동이 가능하도록 설정
// 현재 문제 번호 저장하는 useState Hook
const [quizNum, setQuizNum] = useState(1);
useEffect(() => {}, [quizNum]);
// 퀴즈 번호 이동 관리 함수 코드
const moveToPrev = () => {
setQuizNum(quizNum => (quizNum >= 2 ? quizNum - 1 : 20));
};
const moveToNext = () => {
setQuizNum(quizNum => (quizNum <= 19 ? quizNum + 1 : 1));
};
return (
<MainBody>
<!-- Question : 문제 이미지를 화면에 표시하는 컴포넌트 -->
<Question
src={`./images/questions/q${quizNum}.png`}
width="350px"
height="350px"
/>
<FooterArea>
<!-- QuestionBar : 문제 이동 버튼이 포함된 컴포넌트 -->
<QuestionBar>
<MoveBtn onClick={moveToPrev}>◀︎</MoveBtn>
<QuestionNum>{quizNum} / 20</QuestionNum>
<MoveBtn onClick={moveToNext}>▶︎</MoveBtn>
</QuestionBar>
...
</FooterArea>
...
</MainBody>
);
💡해당 코드 작성 시 마주했던 문제와 해결 방법
- setState의 비동기 처리로 인한 버그로 인해 quizNum(문제 번호)값이 컴포넌트에서 한 템포씩 늦게 변경되는 문제가 있었는데, useEffect의 의존성 배열에 해당 변수를 넣음으로써 이를 해결할 수 있었습니다. 대신 moveToNext, moveToPrev 함수에 문제 번호 이동에 관한 방어 코드를 작성할 땐 quizNum state의 비동기적 특성에 맞춰 로직을 구현했습니다.
■ 답안지 작성 기능
- 시험 중 답안을 작성할 수 있도록 구현
- (답안지에 Toggle 기능을 달아 열고 닫을 수 있도록 함)
- 시험 종료 시 답안지에 작성한 답을 배열로 저장하여 결과 페이지로 전송
// 페이지 이동 Hook
const navigate = useNavigate();
const moveToResultPage = () => {
const data = JSON.stringify(inputValues);
navigate(`/test-one-result`, {
state: {
answersheet: data || '',
nickname: nickname || '학생',
},
});
};
// 답안지 토글 관리 Hook
const [isAnswerSheetOpen, setIsAnswerSheetOpen] = useState(false);
// 답안지 작성 내용 관리 Hook
const [inputValues, setInputValues] = useState(Array(20).fill(''));
useEffect(() => {}, [inputValues]);
// 답안지 토글 관리 함수 2개
const handleAnswerSheetOpen = () => {
setIsAnswerSheetOpen(true);
};
const handleAnswerSheetClose = () => {
setIsAnswerSheetOpen(false);
};
// 답안지 작성 관리 함수
const handleChange = (index, event) => {
const newInputValues = [...inputValues];
newInputValues[index] = event.target.value;
setInputValues(newInputValues);
};
return (
<MainBody>
...
<FooterArea>
...
<BtnArea>
<AnswerSheetBtn onClick={handleAnswerSheetOpen}>
답안지 작성
</AnswerSheetBtn>
<EndBtn onClick={openModal}>답안지 제출하기</EndBtn>
</BtnArea>
...
</FooterArea>
...
{isAnswerSheetOpen && (
<AnswerSheetArea>
<AnswerSheet>
{inputValues.map((value, index) => (
<AnswerSheetElement key={index}>
<span>{index + 1}.</span>
<input
key={index}
type="text"
value={value}
onChange={event => handleChange(index, event)}
/>
</AnswerSheetElement>
))}
</AnswerSheet>
<AnswerSheetCloseBtn onClick={handleAnswerSheetClose}>
답안지 닫기
</AnswerSheetCloseBtn>
</AnswerSheetArea>
)}
</MainBody>
);
💡해당 코드 작성 시 마주했던 문제와 해결 방법
- 특정 버튼을 클릭했을 때, 특정 컴포넌트가 등장했다가 사라지고를 반복하는 Toggle 기능 코드를 바닐라 JS로는 작성할 수 있었지만, React의 JSX 문법을 통해 작성하는 방법은 알 지 못해 어려움을 겪었는데, && 연산자를 활용하여 해당 문제를 해결할 수 있음을 구글링을 통해 학습할 수 있었습니다.
■ 답인지 제출 시, 경고 메세지를 모달창으로 표시해주는 기능
- React의 Modal 컴포넌트를 이용하여 구현
- (prompt 창을 사용하지 않은 이유 : prompt 창이 뜨는 동안 타이머가 일시 정지 하므로)
import Modal from 'react-modal';
Modal.setAppElement('#root');
// 모달 창 Hook
const [isOpen, setIsOpen] = useState(false);
// 모달 창 관리 코드
const openModal = () => {
setIsOpen(true);
};
const closeModal = () => {
setIsOpen(false);
};
const handleConfirm = () => {
// 답안지 제출 확인 버튼을 눌렀을 때 수행할 작업
moveToResultPage();
closeModal();
};
return (
<MainBody>
<FooterArea>
...
<Modal
isOpen={isOpen}
onRequestClose={closeModal}
style={{
overlay: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
},
content: {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
width: '500px',
height: '250px',
border: '5px solid red',
backgroundColor: 'black',
color: 'white',
},
}}
>
<ModalMessage>
정말 시험을 종료하고 답안지를 제출하시겠습니까?
</ModalMessage>
<ModalMessage>
답안지에 오타가 없는지 다시 한 번 확인하시기 바랍니다.
</ModalMessage>
<ModalBtn bgColor="#4b4b4f" fontColor="white" onClick={handleConfirm}>
🆗 예. 답안지를 제출하고 시험을 종료합니다.
</ModalBtn>
<ModalBtn onClick={closeModal}>❌ 아니오. 계속 풀겠습니다.</ModalBtn>
</Modal>
</FooterArea>
</MainBody>
);
💡해당 코드 작성 시 마주했던 문제와 해결 방법
- 처음에는 경고 메세지를 Prompt창을 통해 구현하려고 했습니다. 하지만 그렇게 하려 하니 심각한 문제점이 하나 생겼는데, Prompt창이 실행되는 동안 앱과 함께 타이머가 일시 정지된 상태에서 사용자가 문제를 풀 수 있는, 부정행위가 가능한 상황이 나올 수 있다는 것입니다. 그래서 경고 창이 떠도 앱이 실행될 수 있도록 React의 Modal 컴포넌트를 통해 경고 창을 직접 구현하여 해당 문제를 해결했습니다.
자동 채점 기능 및 채점 결과 표시 기능
■- 답안지의 답과 실제 정답을 비교하여 채점을 진행한 후, 최종 점수와 틀린 문제 번호 등의 정보를 결과 화면에 표시
// 점수 Hook
const [testScore, setTestScore] = useState(0);
const [testRank, setTestRank] = useState('');
useEffect(() => {
calcScore();
}, [testScore]);
// 답안지 초기화 배열
const tempArray = Array.from({ length: 20 }, () => '.');
// 게임 페이지에서 받은 답안지 데이터와 닉네임 데이터를 저장
const answersheet = JSON.parse(
location.state
? location.state.answersheet || JSON.stringify(tempArray)
: JSON.stringify(tempArray)
);
const nickname = location.state ? location.state.nickname || '학생' : '학생';
// 게임 페이지를 방문하지 않은 채 해당 페이지에 접근할 경우, 변수에 빈 값이 할당되어 페이지 로드에 오류가 발생할 수 있으므로
// 변수 초기화 과정이 반드시 필요하다.
// 정답 배열 (코드 일부 생략)
const answer = [
'36',
'REAL',
...
];
// 등급 정보 배열
const rank = [
['S', '100점', '고수 후보생'],
'',
['A+', '90~99점', '정말 잘하심'],
['A', '80~89점', '잘하심'],
'',
['B+', '70~79점', '중수'],
['B', '55~69점', '성장 중'],
'',
['C+', '40~54점', '초보'],
['C', '30~39점', '왕초보'],
'',
['D', '20~29점', '경험 더 필요'],
'',
['F', '0~19점', '낙제'],
];
// 채점 및 최종 점수를 계산하는 함수
const calcScore = () => {
let score = 0;
for (let i = 0; i < 20; i++) {
if (answersheet[i].toUpperCase() === answer[i]) {
score += 5;
}
}
setTestScore(score);
if (score === 100) {
setTestRank('S');
} else if (score >= 90 && score <= 99) {
setTestRank('A+');
} else if (score >= 80 && score <= 89) {
setTestRank('A');
} else if (score >= 70 && score <= 79) {
setTestRank('B+');
} else if (score >= 55 && score <= 69) {
setTestRank('B');
} else if (score >= 40 && score <= 54) {
setTestRank('C+');
} else if (score >= 30 && score <= 39) {
setTestRank('C');
} else if (score >= 20 && score <= 29) {
setTestRank('D');
} else {
setTestRank('F');
}
};
return (
<MainBody>
<HeaderArea>
<Timer>00:00</Timer>
</HeaderArea>
{/* <Instruction src="./images/etc/before-start-test.png" /> */}
<Instruction height="1470px">
<InstructionTitle>▶︎ 테스트 결과 ◀︎</InstructionTitle>
<InstructionList />
<ul>
<InstructionList fontSize="50px">👏🏻</InstructionList>
<InstructionList />
<InstructionList fontSize="17px">수고하셨습니다.</InstructionList>
<InstructionList fontSize="17px">
{nickname}님의 '기초 역량 테스트' 점수는 {testScore}
점입니다.
</InstructionList>
<InstructionList fontSize="17px">
등급은 {testRank}입니다.
</InstructionList>
<InstructionList>
<InstructionList />
<InstructionTitle>■ 채점표 ■</InstructionTitle>
<ResultTable>
<th className="short">번호</th>
<th className="long">제출한 답</th>
<th className="long">정답</th>
<th className="short">정답 여부</th>
{answersheet.map((value, index) => (
<tr key={index}>
<td>{index + 1}</td>
<td>{value}</td>
<td>{answer[index]}</td>
<td>{value.toUpperCase() === answer[index] ? 'O' : 'X'}</td>
</tr>
))}
</ResultTable>
<InstructionTitle>■ '기초 역량 테스트' 등급표 ■</InstructionTitle>
<RankTable>
<th>등급</th>
<th>점수</th>
<th>평가</th>
{rank.map((value, index) => (
<tr key={index}>
<td>{value[0]}</td>
<td>{value[1]}</td>
<td>{value[2]}</td>
</tr>
))}
</RankTable>
</InstructionList>
</ul>
<InstructionList />
<InstructionList />
</Instruction>
...
</MainBody>
);
💡해당 코드 작성 시 마주했던 문제와 해결 방법
- 사용자가 입력한 올바른 영어 답이 대소문자의 구분으로 인해 오답으로 인식되지 않도록 toUpperCase() 메서드를 통해 영어 답을 모두 대문자로 바꾼 후 채점이 진행되도록 구현했습니다.
- 답안지와 닉네임 데이터를 앞 페이지에서 받아오지 못하는 경우 게임 결과 페이지 로딩 시 에러가 발생할 수 있기 때문에, 해당 변수의 초기화를 진행하는 방지 코드를 작성했습니다.
■ 시험 도중 새로고침, 뒤로가기 방지 기능
- beforeunload unload 이벤트를 처리하여 구현
// 새로고침 방지 Hook
useEffect(() => {
const handleBeforeUnload = event => {
event.preventDefault();
// 사용자에게 표시할 메시지
event.returnValue = '';
};
const handleRefresh = event => {
event.preventDefault();
// 사용자에게 표시할 메시지
const confirmationMessage = '';
event.returnValue = confirmationMessage;
return confirmationMessage;
};
window.addEventListener('beforeunload', handleBeforeUnload);
window.addEventListener('unload', handleRefresh);
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
window.removeEventListener('unload', handleRefresh);
};
}, []);
// 뒤로가기 막는 코드
useEffect(() => {
// eslint-disable-next-line no-restricted-globals
history.pushState(null, '', ''); // 현재 페이지 history stack 한개 더 쌓기
window.onpopstate = () => {
// 뒤로가기가 실행될 경우 추가 action 등록
alert('뒤로가기 방지용 알람입니다.');
};
}, []);
💡해당 코드 작성 시 마주했던 문제와 해결 방법
- 사실 해당 기능을 구현하기 위해 어떤 메서드를 호출해야 할 지, 코드는 또 어떻게 작성해야 할 지 전혀 몰랐습니다. 그래서 ChatGPT의 도움을 받아 window.onbeforeunload 이벤트를 활용하여 위와 같은 코드를 작성할 수 있었습니다.
- 앞서 답안지 제출 시 등장할 경고 메시지를 모달 창을 직접 구현한 것과 같은 이유로 (부정행위 방지) 새로고침 방지 알림이 뜨기 바로 전에 화면을 검은색으로 가리게 하고 싶었습니다. 하지만 여러 다양한 시도에도 불구하고 해당 기능은 결국 구현하지 못했습니다. (새로고침 경고 메시지 전에 동작을 수행하는 것은 일반적으로 브라우저의 보안 정책에 의해 제한된다고 합니다.)
(+) 앱 개발 도중 마주했던 마주했던 여러 에러를 해결한 후 그 과정을 깃허브에 따로 기록해두었습니다.
- reportWebVital 관련 문제
- 로컬 폴더와 (비어있는) 원격 저장소를 처음으로 연결하고 푸쉬했는데, 튕겼다
- Fork를 하지 않고 원격 저장소로 push 했더니 403 에러가 발생했다.
- error: pathspec did not match any file(s) known to git 에러 (특정 디렉토리 뿐만 아니라 모든 디렉토리에서 해당 에러 발생)
🧐 <방탈출고사>를 개발하며 느꼈던 점
1. 기획을 어느 정도 마치고 개발 작업에 들어갔을 때, 제 머리 속을 가장 지배하고 있었던 생각은 '내가 이 앱을 주어진 기간 내에 다 만들 수 있을까' 였습니다. 그래서 최악의 상황을 염두해두고 우선 최소한의 기능만 구현하는 것을 목표로 잡은 뒤, 여유가 될 경우 하나씩 추가 기능을 붙여나가는 방향으로 게획을 세웠습니다. 하지만 개발을 실제로 진행하면서 제 생각이 시각적으로 구체화되는 과정을 보니 너무 재미가 있었는지 저도 모르는 사이 몰입을 하며 생각보다 빠르게 기능들을 하나하나씩 완성할 수 있었습니다. 그래서 주어진 기간 안에 추가 기능까지 모두 구현을 할 수 있었습니다. 이를 통해, 역시 내가 좋아하는 일에 몰입하면 어떠한 일이든 다 해낼 수 있다는 것을 다시금 깨닫게 되었습니다.
2. 코드를 작성하면서 한 가지 신경이 쓰였던 점이 있었습니다, 바로 특정 형태의 코드가 반복해서 등장하는 경우가 여럿 있었다는 것이었습니다. 이를 컴포넌트로 묶는다면 좀 더 좋은 코드를 짤 수 있겠다는 생각이 비록 들었지만, 막상 이를 하려니 수정해야 할 부분이 의외로 많아 시간이 오래 걸릴 것 같았습니다. 코드의 품질을 생각하면 당연히 수정을 하는 것이 맞는 선택이겠지만, 당시 부족한 시간으로 인한 압박감으로 인해 결국 '목표한 기능 구현을 다 완성하는 것'을 최우선의 과제로 삼았고, 코드 개선은 나중에 리펙토링을 할 때 진행하기로 했습니다. (나중에 코드 리펙토링 과정을 거친 후 시간이 되면 이에 관한 포스트도 한 번 작성해보겠습니다.)
여기까지 기술적인 측면에서 <방탈출고사>의 개발 후기를 작성해보았습니다. 쓰다 보니 내용이 너무 길어져 이 포스트 하나를 작성하는 데도 며칠이나 걸렸네요. 아직 하고 싶은 말이 많은데, 다음 포스트에선 콘텐츠적 측면에서 앱 개발 후기를 마저 작성해보겠습니다. 읽어주셔서 감사합니다.
🚪 <방탈출고사> 개인 프로젝트 후기 Part 2 바로가기
'✪ 취미, 경험 회고 및 일상 > [회고] IT 관련 경험 회고' 카테고리의 다른 글
신입 개발자 준비에 대한 고민, 그리고 협업 중 일어난 갈등상황을 겪으며 쓰는 개인적인 성찰 (0) | 2024.02.26 |
---|---|
비전공자 수준도 안되었던 어느 컴공생의 2022년 회고 (4) | 2024.01.03 |
🦁 멋쟁이 사자처럼 11기 합격 후기 (서류, 면접) (0) | 2023.03.22 |
위코드 2차 팀 프로젝트 'WeMong' 회고 (0) | 2022.12.11 |
위코드 1차 팀 프로젝트 'HealthEat' 회고 (0) | 2022.11.27 |