티치몬 회고록

오주현·2025년 3월 29일

티치몬이란? 부산소마고 "자습 감독 및 방과후 이석 관리 서비스"

팀원 구성 계기

24년 11월 말 즈음 H와 함께 전공동아리 팀원을 구성하자는 것에서 시작되었다. 필자의 친구 한 명(J)과 H의 친구(Y)와 함께 4인 팀을 꾸렸는데, 필자와 J, Y의 포지션은 프론트엔드였다. H의 포지션은 백엔드로 팀원 구성상 백엔드 한명이 더 있으면 좋겠다고 생각해 H와 필자의 친구인 K를 데려와 5인팀이 구성되었다.

이렇게 되니 디자인할 친구가 부족한 상황이 발생하였다. 그러나 6인 팀은 인원이 많은 거 같아 프론트엔드에서 디자인까지 맡게 되었다.

팀원 구성 이후

H와 Y가 디자인 시간에 프로젝트로 진행한 티치몬과, K가 구상해 온 학원 프로젝트 2개가 나와 H, Y 따로, J, K, 필자 이렇게 따로 프로젝트를 진행하자는 의견이 나왔다. 하지만 시간관계상 2개의 프로젝트를 따로 나눠서 하는 것보단, 5명 같이 하나의 프로젝트를 하자는 의견이 나왔다. 기획과 디자인이 완료된 티치몬을 선정해 방학 동안의 프로젝트를 시작하게 된다. 시기 좋게 이때, AI 경진대회 공모전도 나와 이 공모전에도 티치몬으로 참가하게 된다.

회의

티치몬 개발기간이 1월 초 ~ 2월 초 사이인 점을 감안하여 매일 정기 회의 시간을 21시 ~ 23시로 잡고 진행하였다. 정의는 21시 ~ 23시였지만, 대부분 새벽을 넘겨가며 회의를 진행했다. 공휴일, 주말 예외 없었다. 특별한 사정이 아니였으면 무조건 회의는 필참이였다. 이 회의에서는 서로 각자 개발하거나 수정해야할 부분 등 여러가지를 진행했다.

개발

기술스택

코어: React, Javascript
상태 관리: React-Query, Zustand
스타일링: Styled-components
패키지 매니저: npm
CI/CD: GitHub Actions

나는 24년 중반 즈음 나는 리액트의 중요성을 깨닫고 기본기만 알고 있던 상황이였다. 그러나 티치몬에서 Zustand? Styled-components? 이게 뭐지 하는 다양한 라이브러리, 프레임워크를 사용했다. 나는 퍼블리싱밖에 해보지 않았기에 이 부분에 대해서도 정확히 알지 못했다. 그러나 역시는 역시였다. 개발은 프로젝트를 하며 길러가는 것이였다.

프레임워크와 라이브러리는 써보면서 실력을 기르는 것이다. 아예 몰랐던 나도 Y가 써둔 코드를 따라쳐보면서, "아 이렇게 하는 거구나" 하며 배운 것이 정말 많았다. 아래는 내가 처음 Styled-components 코드를 작성한 것이다.

 import styled from "styled-components";
 import (... 생략)
 
 export const ConfirmContainer = styled.div`
   width: 120px;
   height: 42px;
   color: white;
   display: flex;
   justify-content: center;
   align-items: center;
   gap: 10px;
   border-radius: 10px;
   background-color: ${(props) => (props.color === 'red' ? '#F87067' : props.color === 'blue' ? '#2E6FF2' : '')};
   cursor: pointer;
   &:hover {
     opacity: 0.95;
   }
 `;

정말 어려운 것은 아니였다. 단지 시도를 안해본 것뿐...

서버와의 통신도 나는 티치몬을 하면서 처음 해보는 것이였다. axios를 사용해야 한다는 건 알았지만 Y친구가 axiosInstanse라는 걸 만들어 두었고 이걸 사용하라 하였다.

처음엔 이게 뭔지 몰랐지만 설명을 들어보니 "이렇게 쓰는게 훨씬 좋구나" 라는 생각을 하게 되었다. (자세한 코드는 깃허브 레포지토리를 참고하면 좋을 것같다.)

또한 상태관리는 React-query를 사용하게 되었는데 이 것 또한 난생 처음보는 거였다. 왜 쓰는 건지 정말 의아해했는데 찾아보고 공부해보고 하니 React-query를 쓰는게 훨씬 완전 개 이득이란걸 알게되었다.

아래 코드는 내가 처음 axios통신하는 코드와 React-query 코드를 짠 것이다.

 import axiosInstance from "../lib/axiosInstance.js";
 
 export const autoAssignment = async ({start, end}) => {
     try {
         const res = axiosInstance.post(`${API_ENDPOINTS.SUPERVISION}/assignment`, {
             start: start,
             end: end
         })
         if (res.status !== 200 && res.status !== 201) {
             return Promise.reject({
                 status: res.status,
                 message: res.message || 'Request failed'
             });
         }
         return res;
     } catch(err) {
         return Promise.reject(err);
     }
 }
 import { useMutation, useQuery } from '@tanstack/react-query';
 import { useNavigate } from 'react-router-dom';
 
 export const useAutoAssignment = () => {
     const navigate = useNavigate();
     return useMutation({
         mutationFn: (props) => API.autoAssignment(props),
         onSuccess: () => {
             navigate('/supervision/detail');
         },
         onError: (err) => {
             console.error('Assignment 실패:', err);
         }
     });
 };

이것 또한 정말 어려운 것은 아니였다. 단지 사용법과 개념을 몰랐을 뿐.

💢트러블슈팅

export const useGetMonthlySupervision = (month) => {
    return useQuery({
        queryKey: ['getMonthlySupervision', month],
        queryFn: async () => {
            const res = await API.getMonthlySupervision(month);
            return res.data || [];
        }
    })
}

아래 통신을 진행하면서 였다.

처음엔

import { useGetMonthlySupervision } from
'../../../hooks/useChange.js';

const { data: TeacherList, isLoading, isError }
= useGetMonthlySupervision(currentMonth);

이런식으로 가져다 사용하였다. 그러나 정상 작동을 하지 않았다. 이유를 찾아보기 위해 API명세서를 확인해보았는데,

{
	"data" : [
		{
			"week" : "2월 1주차",
			"day" : "2월 1일 (월)",
			"date" : "2025-02-11",
			"self_study_teacher": {
				"7th_teacher": "이름/1/me",
				"8th_teacher" : "이름/2",
				"10th_teacher" : "이름/3"
			},
			"leave_seat_teacher": {
				"7th_teacher": "이름/1/me",
				"8th_teacher" : "이름/2",
				"10th_teacher" : "이름/3"
			},
			"night_teacher": "이름/1/me"
		}
	]
}

위와 같이 응답이 오는 것이였다.
"어 그럼 응답 받아오는 곳에서 res.data를 반환해주는데 정상적으로 바로 떠야하는거 아닌가? 근데 왜 흰 화면이 가끔씩 뜨지?"
이렇게 생각했다. 그러나 문제가 있었다.

React Query는 데이터를 비동기적으로 받아오기 때문에 컴포넌트가 처음 렌더링될 때는 아직 data가 할당되지 않아 undefined 상태가 된다. 따라서 바로 data에 접근하면 "undefined" 에러가 발생할 수 있다.

예를 들어, 아래와 같이 작성하면

const { data: TeacherList, isLoading, isError } = useGetMonthlySupervision(currentMonth);

초기 렌더링 시 TeacherList는 undefined가 될 가능성이 있다. 그 상태에서 TeacherList.data에 접근하면 에러가 발생하게 된다.

const { data: TeacherList = { data: [] }, isLoading, isError } = useGetMonthlySupervision(currentMonth);

만약 data가 undefined라면 TeacherList에 기본값인 { data: [] }가 할당된다. 이렇게 하면 렌더링 시점에서 TeacherList.data를 안전하게 사용하여 undefined 에러를 예방할 수 있다.

즉, 초기 데이터가 아직 로드되지 않은 상태에서도 기본값이 지정되어 컴포넌트가 문제없이 렌더링될 수 있도록 한 것이다.

프로젝트를 거의 끝맺음 하며...

프로젝트에서 가장 중요한건 역시나 협업인거 같다. 서로 소통이 잘되고 불화를 일으키지 않으며 원활히 잘 진행된 것도 우리 프로젝트에서 한 건을 한거 같다.

티치몬 개발을 마무리 하고 배포를 진행하고 사용자를 받아 운영해보니 힘든 점도 꽤 많았다. 요구사항, 버그, 에러 등등...
이렇게 큰 프로젝트를 처음 해보면서 배운점, 깨달은 점이 매우 많은 것 같다. 아무것도 안하는 것 보단 팀을 꾸려 프로젝트를 해보는게 개발 인생에서 매우 중요한 것 같다.

15개의 댓글

comment-user-thumbnail
2025년 3월 29일

너무 감동적인 회고록이에요!@!@!@!@!@!@!@!@

2개의 답글
comment-user-thumbnail
2025년 3월 29일

역시 개발은 프로젝트를 하면서 실력이 느는 것 같아요.
이번 프로젝트 진행 하시면서 성장하신 점이 돋보여요.
저도 본받아야 할 것 같아요!
화이팅입니다

1개의 답글
comment-user-thumbnail
2025년 3월 29일

저도 평소에 다른 사람들 깃허브를 보며 배우는게 많은 입장으로써 공감이 되네요!
잘 읽었습니다!

2개의 답글
comment-user-thumbnail
2025년 3월 30일

우와 잘읽었습니다 앞으로도 화이팅👏🔥

1개의 답글
comment-user-thumbnail
2025년 3월 31일

굿굿 2학년 때 도메인이 큰 프로젝트 하면 포트폴리오에 정말 많은 도움이 되는거 같아요,,
2학년 화이팅!!

1개의 답글
comment-user-thumbnail
2025년 4월 1일

멋져용

1개의 답글
comment-user-thumbnail
2025년 9월 25일

안녕하세요 하숙집입니다

답글 달기