교내 학생 관리 시스템, BSM 회고록

우빈·2023년 11월 28일
36
post-thumbnail

개발 동기

쓰는 회고록마다 꾸준히 하는 이야기같지만.. 필자의 학교는 조금 특이하다.
나는 부산소프트웨어마이스터고등학교에 2학년으로 다니는 학생이다.

우리 학교는 학생들이 일과 시간에도 노트북을 굉장히 많이 이용한다.
따라서 학교 관련 학사 일정이나 급식, 혹은 교내에서 운영하는 *인증제 정보를
노트북으로 확인할 때가 많다.

그런데 이런 정보들을 확인해야할 때 학교에서 제공하는 사이트를 확인하는 것은 너무 번거로웠다.

*인증제 : 우리 학교 교내에서 학생들의 활동(ex) 자격증 취득, 대회 수상 등)에 점수를 매겨 평가하는 제도로, 특정 학생들을 선발해야할 때도 평가되는 중요한 제도이다.

1. 사이트가 분산되어있다

학사 일정과 급식 사이트는 학교 홈페이지에 있고, 마이스터역량인증제 사이트는
또 다른 도메인에 있는 둥, 학생들이 원하는 정보를 얻기 위해 쉽게 유영하기가 어려운 구조로 사이트가 설계되어있다.

2. UX/UI가 조잡하다

우리 학교에서 제공하는 사이트들의 UX/UI를 간단하게 알아보자.

인증제 페이지

급식

학사 일정

ㅋㅋ 그만 알아보자.

3. 커뮤니티 활성화

또한 이런 기존 사이트를 대체하는 것 뿐만 아니라, 학생들끼리 대화를 나누고
할 수 있는 공간도 있으면 여러 정보를 공유하는 등 매우 유용하게 사용될 수
있을 것 같았다.

4. 기숙사 서비스

우리 학교는 특이사항이 아니면 학생 전체가 기숙사에 입사하는 것이 원칙이다.
그래서 학교 못지 않게 학생들이 오래 있는 곳이 기숙사인데, 기숙사에서 서비스가
따로 필요한 기숙사 입사 확인이나 학습실 예약 등의 기능도 있으면 좋을 것 같았다.

결론?

그래서 평소에 학생들을 위한 플랫폼 개발 동아리라는 타이틀을 걸고 있었던
우리 동아리 Team.INSERT는 이런 플랫폼을 개발해 학생들의 생활을 한 단계 더
편리하게 해주자 라는 생각을 했다!

여기서 잠깐!

이 회고록은 나, 즉 프론트엔드 개발자로 참여한 팀원 쓴 회고록이다.
따라서 프론트엔드와 관련된 기술을 중점으로 회고록을 서술할 것이다.

프로젝트 소개

내가 작성해놓았던 사용자들에게 프로젝트를 소개하는 BSM 내 공지사항 내용을
가져와서 포스팅했다.

어떤 서비스를 지원하는지 서술되어있기에 참고 바란다!!

서론

새로운 BSM이 출시되었습니다!
사용자들은 다음과 같은 서비스를 해당 사이트 내에서 이용할 수 있습니다.

마이스터 역량 인증제

학번을 입력해 해당 학생의 인증제 점수를 확인할 수 있습니다.
데이터 시각화된 자료를 바탕으로 정보를 조금 더 손쉽게
읽을 수 있습니다.


커뮤니티

기존 BSM과는 다른 방식으로 커뮤니티를 사용할 수 있습니다.
사용자는 게시판, 공지사항, 프로젝트, 코드 리뷰, 찾았어요, 잃어버렸어요 까지 총 6개의 카테고리를 사용해 글을 작성할 수 있습니다.

게시판

글 제목과 글 내용을 입력해야 합니다.

프로젝트

글 제목과 프로젝트의 분야, 시작 기간, 마감 기간, 글 내용을
입력해야 합니다.

코드 리뷰

글 제목과 리뷰받을 PR의 URL, 글 내용을 입력해야 합니다.

찾았어요

글 제목과 습득 장소, 보관 장소, 습득물 이미지를 입력해야 합니다.

잃어버렸어요

글 제목과 분실 장소, 분실물 이미지를 입력해야 합니다.



이렇게 작성된 글에는 댓글과 답글을 작성, 수정, 삭제할 수 있습니다.

대나무숲

익명으로 하고 싶은 이야기를 마음껏 풀 수 있는 대나무숲입니다.
모든 글은 익명으로 처리가 되며, 이에 관하여 서버측에서는
로그나 DB에 유저의 정보가 남지 않습니다.

오늘의 급식

급식을 확인할 수 있습니다.
키보드에 위치한 화살표 또는 화면의 화살표를 눌러 날짜를
변경하며 조회할 수 있습니다.

캘린더

학생들은 캘린더를 사용할 수 있습니다.
학생들은 해당 캘린더에 자유롭게 일정을 추가할 수 있으며,
일정은 학급 일정, 학년 일정, 학교 일정으로 분류됩니다.

학급 일정은 일정을 등록한 학생의 학급 구성원들만 조회할 수 있습니다.
학년 일정은 일정을 등록한 학생의 학년 구성원들만 조회할 수 있습니다.
학교는 교내 모든 학생들이 일정을 조회할 수 있습니다.

베르실 예약

베르실을 예약할 수 있습니다.

입사 체크

그간 번거로웠던 입사 체크를 BSM에서 한 번의 클릭만으로
체크할 수 있습니다.
가짜 체크 방지를 위하여 bssm_free 와이파이를 사용하고 있을 때에만
체크가 가능합니다.

사용한 기술들

프로젝트 회고를 개발 순서대로 질질 끌지 않고, 간단하게 사용한
기술(의존성)들에 대해 하나하나씩 언급하며 무엇을 어떻게 사용했는지 이야기해볼까 한다.

package.json

다음은 BSM의 package.json이다.

{
  "name": "bsm_web_v3",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next lint && next build",
    "start": "next start",
    "lint": "next lint",
    "test": "yarn && yarn build && yarn lint && yarn install --immutable --immutable-cache --check-cache && npx playwright install --with-deps && npx playwright test"
  },
  "dependencies": {
    "@tanstack/react-query": "^4.35.3",
    "@uiw/react-markdown-preview": "^4.1.16",
    "@uiw/react-md-editor": "3.6.0",
    "apollo-link-token-refresh": "^0.6.0",
    "axios": "^1.4.0",
    "d3": "^7.8.5",
    "dayjs": "^1.11.9",
    "dotenv": "^16.3.1",
    "graphql": "^16.8.0",
    "graphql-request": "^6.1.0",
    "jotai": "^2.5.1",
    "jwt-decode": "^3.1.2",
    "msw": "^1.2.3",
    "next": "13.5.2",
    "next-remove-imports": "^1.0.12",
    "react-circular-progressbar": "^2.1.0",
    "react-d3-radar": "^1.0.0-rc6",
    "react-dom": "18.2.0",
    "react-scroll-horizontal": "^1.6.6",
    "react-slick": "^0.29.0",
    "react-spinner": "^0.2.7",
    "react-spinner-overlay": "^0.1.33",
    "react-spinners": "^0.13.8",
    "react-toastify": "^9.1.3",
    "rehype-sanitize": "^6.0.0",
    "slick-carousel": "^1.8.1",
    "styled-components": "^6.0.8",
    "sweetalert2": "^11.7.28",
    "tinymce": "^6.6.0",
    "typescript": "5.0.4"
  },
  "config": {
    "commitizen": {
      "path": "./node_modules/cz-conventional-changelog"
    }
  },
  "msw": {
    "workerDirectory": "public"
  }
}

사실 진짜 package.json은 더 많은데 설명과 관련없는
eslint, prettier 그 외 devDependencies는 제외하고 가져왔다.

@tanstack/react-query

리액트 쿼리를 사용해서 사용자가 API를 불러올 때
계속해서 요청하지 않고 사이트 내 데이터를 캐싱하여 불러올 수 있게 하기 위해 사용했다.

리액트 쿼리에서 제공하는 mutate 후 onSuccess나 onError등의 메서드들을 통해
실행 후 발생해야 하는 이벤트들을 실행시켰다.

사용자가 mutation을 할 때, refetch 메서드보다는
모든 데이터를 삭제하고 다시 값을 가져오는 invalidateQueries 메서드를 주로 사용했다.

솔직하게 onSuccess, onError 함수를 야무지게 사용하는 1인이었는데
새로 나온 리액트 쿼리 버전에서 이런 함수들이 사라진다는게 좀 슬플 뿐이다ㅠ

@uiw/react-markdown-preview, @uiw/react-md-editor

학생들의 전공 분야가 코딩인 만큼, 마크다운을 사용하되 백틱 세개를 통해
코드를 입력할 때 코드가 각 언어마다 색깔, 포매팅 등이 예쁘게 보여야 한다고
생각했다.

따라서 여러 라이브러리중 가장 에디터 UI가 기본적으로 깔끔하고,
언어마다 코드에 색을 이쁘게 넣어주는 uiw react markdown 뷰어와 에디터를 사용했다.

주로 사용된 도메인은 게시판으로, 사용자가 글을 작성할 때 제공하는 템플릿을
개발자들이 많이들 사용하는 마크다운으로 지정했다.

axios

axios를 베이스로 API를 구축했다.
보통은 customAxios나 기본 axios를 사용하여 API를 구축하는데,
우리 팀은 httpClient라는 커스텀 클래스를 생성하여 그 안에 customAxios와
리퀘스트 인터셉터, 리스폰스 인터셉터들을 넣어주었다.

이렇게 클래스로 감쌌을 때의 장점은 여러가지이다.

  1. 유지보수가 간편해진다. 한 클래스 내에서 모든 API가 감싸져 있기에
    변경 사항이 생길 경우 해당 클래스만 수정하면 된다.
  2. 커스텀하게 필요한 메서드들을 구현하여 사용할 수 있다.
    get, put, post, delete등의 메서드를 기본적으로 이용하는 데에서 나아가
    원하는 서비스에 맞게 쿼리를 작성해서 메서드를 만들어 사용할 수 있다.
    이렇게 되면 코드 가독성이 높아지고, 유지보수가 더욱 용이해진다.
  3. 라이브러리에 문제가 생겼을 때 쉽게 변경이 가능하다.
    혹여나 axios라는 라이브러리에 문제가 생겼을 때, 모든 코드를 뜯을 필요 없이
    우리가 선언해준 클래스 내에서 axios만 덜어내고 다른 라이브러리를
    사용하면 되기에 쉽게 변경할 수 있다는 장점이 있다.

이 개발 구조에 대해 더욱 자세히 알기를 원한다면 밑의 링크로
코드를 직접 보는 것을 추천한다 !!

https://github.com/Team-INSERT/bssm-frontend/blob/main/src/apis/httpClient/httpClient.ts

d3, react-d3-radar

js에서 그래프를 그릴 수 있게 해주는 d3와 react-d3-radar를 사용했다.
d3로 인증제 점수를 평균 점수와 교내 최고점 점수를 비교해서 그래프로 보여주게 설계했다.

또한 레이더 차트가 필요해서, react-d3-radar라는 리액트에서 레이더 차트를
사용할 수 있게 해주는 라이브러리를 같이 사용해서 시각화했다.

d3 코드는 다음과 같다!
https://github.com/Team-INSERT/bssm-frontend/blob/main/src/templates/meister/chart/MeisterChart.tsx

dayjs

기본적인 Date 클래스를 사용해서 날짜를 파싱하는 데에는
코드가 더러워지기도 하고 리팩토링이 어렵다는 점도 있었다.

그래서 외부 라이브러리를 사용하기로 했는데, moment는
dayjs에 비해 비교적으로 최근 업데이트일도 늦고, 옛날에
출시된 라이브러리였기에 dayjs를 채택했다.

graphql, graphql-request

위에서 설명한 것처럼, 게시판의 카테고리마다 다다른 값들을
필요로 했기 때문에, 클라이언트에서 카테고리를 기준으로
값들을 불러오기 위해 클라이언트가 원하는 대로 요청값을
변경할 수 있는 graphql을 사용했다.

고생 많이했다....
graphql 한줄평은 초급자가 덤비면 너무 힘든 라이브러리 같다.
초반에는 apollo-client를 사용했는데, 라이브러리 특성상
클린 코드를 설계하기가 너무 복잡해서 포기하고 graphql-request를 사용했다.

또한 apollo-client를 react-query와 같이 사용하니
데이터가 이중으로 캐싱되는 바람에 곤란한 경우가 발생해서,
결국 apollo-client를 삭제하기로 결정했다.

jotai

요즘은 recoil보다 zustand, jotai 등의 상태관리
라이브러리들이 유행하고 있는 추세이다.

우리 팀이 jotai를 사용한 이유는 간단하고 명시적이었기 때문이다.
상태를 선언할 때 key를 주지 않고도 선언할 수 있으며,
제공하는 메서드들의 네이밍 또한 매우 명시적이고 간단했기에
원래 recoil을 사용하다가 jotai로 마이그레이션하여
코드의 양을 조금이나마 줄이고 명시적으로 코드를 바꾸었다.

react-circular-progressbar

이 또한 인증제 페이지의 데이터 시각화를 위해 사용했다.
각 역량마다 성취도를 퍼센트로 나타내기 위해 사용했다.

react-spinner

게시판에서 무한 스크롤을 구현하여, 게시판을 스크롤할 때
글을 불러오거나 인증제 페이지의 데이터들을 로딩하는 중간 사이에
로딩 스피너를 보여주게하기 위해서 사용했다.

react-toastify

사용자에게 특정 이벤트가 발생했을 때 알려주기 위해서 사용했다.
보통 대부분의 이벤트에 해당 라이브러리를 사용했다.

alert나 modal등을 사용하여 정보를 전달하지 않은 이유는,
이미 발생한 이벤트의 정보를 제공하려하는 데에 alert나 modal을 이용해서
사용자가 보고 있는 화면을 가려버리거나 플로우를 중단하게 해버리면
이 또한 UX를 해칠 것이라고 판단하여 오른쪽 위에 뜨게 하는 toastify를 사용했다.

sweetalert2

위에서 서술한 것과 반대로 사용자에게 confirm을 받을 경우에는
해당 라이브러리를 사용했다.

보통 글이나 댓글, 답글, 등록해놓은 일정 등을 삭제할 때 삭제를 확인하는
용도를 주로 사용하여 안전한 UX를 제공하는 식으로 설계했다.

rehype-sanitize

이건 교내 학생 중 한명이 베타 테스트를 진행하던 때에 HTML, CSS 코드를
악성으로 넣어서 화면의 오버레이에 접근해 구성되어있던 레이아웃을 다깨부숴버린 후로
이를 보완하기 위해 사용한 라이브러리이다.

XSS 공격을 막아주는 라이브러리로, @uiw/react-md-editor와 preview에
플러그인을 추가하여 적용했다.

그 후론 테스트를 해보아도 사이트 보안이 뚫리거나 하는 일이 없어졌다!
이 라이브러리 만드신 분 존경한다 진짜ㅠㅠ

styled-components

스타일드 컴포넌트(이하 sc)를 사용했다. 저번에 진행했던 프로젝트에서는
sc를 또 다른 파일로 빼서 묶어놓았으나, 이번 프로젝트에서는 sc를 사용하는
코드 밑에 sc를 선언해두는 태그를 놓아 작업을 하거나 코드를 볼 때
어떤 스타일링이 적용되어있는지 빠른 시간 내에 확인할 수 있게 설계했다.

typescript

프로젝트가 생각보다 컸기에, props 드릴링이나 함수 타입에 이상한 타입의
파라미터가 들어가 사용자가 실행하는 단에서 에러가 발생하는 것을 막기 위해
타입스크립트를 사용했다.

이를 통해 안전하게 코드를 작성할 수 있다는 장점을 얻었지만,
여러가지 인터페이스를 사용하는 등 코드가 늘어난다는 단점이 있었다.

그래도 난 타입스크립트를 사랑하는 걸~
가끔 라이브러리랑 타입이 일치하지 않아 화나는 경우도 있지만
에휴 어쩌겠니

next.js

Next.JS의 app 라우터를 사용해서 설계했다.
특별한 점은 컴포넌트로 빼지 않고 마치 모노레포를 사용한 것처럼
페이지, 즉 도메인마다 해당 도메인에서 사용하는 여러가지 모듈들을
한 디렉터리 내에 모아두는 방식을 선택했다.

이는 밑의 리팩토링 단계에서 더욱 자세하게 서술하겠다.

마무리

이런 여러가지 라이브러리들을 사용하며 프로젝트를 개발했다.
내 인생 이렇게 많은 라이브러리들을 사용해본건 처음이다.
친구도 레포지토리보고 경악하더라...

베타 테스트 버전 배포

기본적인 Sanity Test를 거친 후, 5명 정도의 베타 테스터를 모집하여
베타 테스트 버전을 배포했다.

QA 시트를 만들어서 베타 테스터들에게 이슈나 요구 사항이 있으면
해당 스프레드시트에 이슈를 추가해달라고 요청했고,

짧은 시간 내에 약 50개 가량의 이슈가 모였다.

그리고 주말에 6시간 동안 한 자리에 앉아서 버그들을 픽스한 결과...
하루 만에 모든 이슈들을 픽스했다..ㅎㅎ 뿌듯뿌듯 ~

리팩토링

내 인생 최대 고비였다. 하 지금 생각해봐도 너무 힘들다

리팩토링 전

원래는 이런식으로 src 내에 전역으로 컴포넌트, constant, hook, interface 등을 선언해두고 모든 도메인에서 사용하는 것들을 가져오는 식으로
구조를 설계했다.

그리고 도메인들은 page라는 디렉터리에 레이아웃과 API 통신과 관련된
모듈들을 넣어두는 식으로 진행했다.

app 디렉터리를 사용하며 pages라는 디렉터리명에 이런 도메인들을
정리해두고 싶었으나, pages와 app 디렉터리를 동시에 사용하면서
이 둘 간의 라우터가 충돌될 경우 오류가 발생해 어플리케이션이 실행되지
않았기에 임시적으로 page라는 디렉터리를 사용했다.

after

리팩토링 후에는 templates라는 디렉터리가 기존 page 디렉터리를 대신한다.
또한 전역으로 사용하던 여러 모듈들을 templates의 각 도메인마다 넣어주었다.

전역적으로 사용되는 모달과 user와 관련된 값들은 디렉터리로 묶어서 처리했다.
이로써 모달이나 유저와 관련된 도메인에 수정이 필요할 때 또한 여러 곳을
돌아다니지 않고 해당 디렉터리에서 개발하여 시간을 절약할 수 있다.

이로써 한 도메인에 대한 작업을 할 때, 전역에서 여러 디렉터리들을
유영하며 개발하지 않고 한 디렉터리 내에서 모든 작업을 처리하여
추가적인 개발을 진행할 때 시간을 절약할 수 있도록 설계했다.

templates 내 디렉터리 구조의 모습이다.

모든 프로젝트의 구조가 어떤지는 모르겠으나, 기존에 내가 진행했던
프로젝트의 구조나 지인 개발자들이 사용하는 구조와 매우 달랐었다.

서비스를 처음 설계할때에도 도메인이 너무 컸기에 이런 방식을 써보자고
생각했었는데, 프로젝트 초반에는 어떤 모듈들이 추가될 지 예상하기도 어렵고
너무 추상적이어서 포기했다가 리팩토링을 하면서 바꾸게 되었다.

할 거면 처음부터 하는 것을 추천한다

솔직히 이거 하는 동안 정신병 걸릴뻔 했다.

리팩토링을 한 도메인마다 한 PR씩 순회해가며 코드의 가독성을 높였고,
마지막으로 모든 구조를 뒤엎어 리팩토링을 끝냈다.

Files changed가 419인 것을 보면 왜 내가 리팩토링 대신
구조를 처음부터 정해놓고 가는 것을 추천하는지 알 수 있다.

그리고 커밋은 제때제때 해두자... 커밋하는데 몇 십분 걸리더라.....

정식 출시

AWS에 해당 서비스들을 배포하여 사이트 개발을 끝냈다.
학교에서 진행하는 전공 동아리 부스 운영과 겹쳐있어서,
해당 행사에서 우리의 프로젝트에 대한 PPT 발표를 진행하고 부스를 운영했다.
우리는 최종적으로 교내 동아리 13팀중 3위를 달성해 장려상을 받았다.

더 높은 순위를 기대하기도 했었는데, 물론 수상해서 너무 기뻤지만
아주 조금은 아쉬운 감이 없지 않아 있었다.

오픈 소스 전환

기여해주는 사람이 많지는 않더라도, 우리의 레포지토리를 오픈 소스로 전환해
교내 학생들이 직접 원하는 기능을 제보하거나 버그를 픽스할 수 있도록 계획했다.
더 나아가 다른 사람들이 이 프로젝트에 기여할 수 있도록 하는 것이 목표이다.

전환하는 과정이 그렇게 어렵지는 않았다.
오픈 소스로 전환하는 방법이 무엇인지 몰라서 toss/slash 레포지토리를
보며 이를 기반으로 오픈 소스 소개나 규칙을 작성했다.


전환한 당일날 교내 학생 한 명이 docs와 관련된 내용을 기여해주었다.
기여해주는 사람이 많진 않더라도 나름 뿌듯했다.

현재 기여자가 8명인데, 내가 졸업하기 전까지 15명만 넘으면 좋겠다!!
조금 큰 꿈일지도 ㅎㅎ..

깨달은 점

정해놓은 데드라인을 지키느라 발등에 불이 떨어져서 고생을 했다.
또한 graphQL이라는 사용해보지 않은 기술을 사용하다보니
새로 여러가지의 오류들도 겪어보곤 했다.

사용해보지 않았던 도메인과 기술들을 사용하며 개발을 진행했던 것 같다.
또 급할 때에는 내가 직접 백엔드 코드를 보며 dto를 변경하는 등
미미하게 백엔드 레포지토리에도 기여를 하며 개발했다.

생각보다 흥미가 있었고 내가 프론트엔드 개발을 하면서도 시간이 날 때마다,
필요성을 느낄 때마다 여러가지 기술 스택들을 배워보며
기술 스택에 제약을 받지 않는 개발자가 되어야겠다는 생각을 했다.

마무리

이제 마지막으로 진행 중인 프로젝트만 하면 이번 년도와 내년의 프로젝트는
끝나고, 슬슬 취업 준비를 시작할 것 같다.

지금도 사용자들을 위해 프로젝트를 개발하는 모든 개발자들에게 찬사를 보내며,
다들 행복하고 건강부터 챙기며 개발을 진행하기를 바란다.

화이팅!

profile
프론트엔드 공부중

6개의 댓글

comment-user-thumbnail
2023년 11월 28일

불편한 점을 잘 찾아서 예쁘게 개발하셨네요. 서비스 사용 기술 같은 것도 하나하나 이유가 있어서 좋아요! 부마고가 아니라서 직접 사용을 할 수는 없지만, 오픈 소스... 기여해보고 싶네요!!

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

너무 깔끔하게 잘 만드셨네요! 궁금한점이 있는데 백엔드 기술은 어떤게 사용되었나요?

1개의 답글
comment-user-thumbnail
2023년 12월 11일

와우.. 많은 라이브러리를 다 이해하고 쓰기 힘드셨을텐데 고생 많으셨네요. 엄청 깔끔하게 짜서 참고하기도 좋겠네요!

1개의 답글