개발/KB IT's Your Life 7기

KB IT's Your Life 7기 - 스켈레톤 프로젝트 후기

Lylica 2026. 4. 19. 22:56


이번 주의 주요 내용은, 지난 포스팅에서도 언급했던 것처럼 스켈레톤 프로젝트에 관한 글이다. 

 

내가 이 부트캠프에 참여하고 싶었던 이유 중 하나가 바로 팀 프로젝트였다.

 

나는 지금까지 프로젝트 경험이 없었다... 고 말할 수 있을 정도로 개발 프로젝트를 한 것이 손에 꼽는다.

 

그래서 내게는 KB IT's Your Life에서 제공하는 프로젝트의 경험이 굉장히 매력적으로 느껴졌다.

 

대략 1달동안 프론트엔드 개발을 위해서 배웠던 모든 것을 보여주는 시간이 되시겠다.

 

스켈레톤 프로젝트가 뭔데?

Spooky Scary Frontend

 

스켈레톤 프로젝트는 말 그대로 뼈대만 만들어진 프로젝트 템플릿이라고 생각하면 된다.

 

기능은 구현되어 있지 않고, 우리 개발자들이 그 위에 실제 기능을 채워 넣도록 설계된 구조다.

 

마치 뼈다귀에 살을 채워 넣는 것처럼 말이다.

 

물론 그렇다고 그냥 코드를 제공해주지는 않는다. 단순히 기능 명세를 적어줄 뿐이다.

 

 

하지만 우리가 프로젝트를 만들 때 가장 중요하게 보아야 하는 것도 역시 기능 명세다. 

 

기능 명세는 시스템이 어떤 기능을 해야 하는지를 구체적으로 정의한 문서라고 생각하면 된다.

 

개발자마다 해석이 달라지는 문제를 해결할 수 있고, 동작의 흐름을 바로 이해할 수 있기 때문이다.

 

 

우리 스켈레톤 프로젝트는 코드를 제공하지 않는 대신, 명확한 기능 명세를 제공해준다.

 

스켈레톤 프로젝트 기능 명세

 

우리 스켈레톤 프로젝트의 주제는 가계부 앱 만들기 되시겠다.

 

기능 요구 사항과, 디자인 요구 사항, 기술적 요구 사항을 나누어서 각각의 상세 내용을 제공해주셨다.

 

위와 같은 구체적인 목표를 받고 난 후, 드디어 스켈레톤 프로젝트가 시작되었다.

 

 

조 편성

팀 프로젝트를 위해서는 역시 조 편성을 해야지.

 

조 편성은 각 회차(반) 에서 인원을 나눠 결정한다.

 

우리 회차 강사님은 제비뽑기를 직접 구현해서(???) 뽑아주셨다.

강사님이 제비를 뽑는 방법

 

위 코드는 우리가 배운 기술들을 바탕으로 강사님이 구현한 제비뽑기 코드다.

 

복습하는 겸, 코드를 한 번 간단하게 살펴보자.

export const useSkeletonStore = defineStore('skeleton', () => { ... });

 

Vue 수업 중, 상태 데이터를 전역으로 저장하는 방식으로 Pinia를 배웠다.

 

위 코드는 Vue에서 사용하는 Pinia의 Composition API 방식 Store 를 사용하는 방법이다.

 

`skeleton` 이라는 이름의 전역 상태 저장소를 생성하는 일을 해준다.

 

이제 `useSkeletonStore`를 사용하면 어디에서든지 상태 데이터에 접근할 수 있다.

const members = [...]

 

이곳에는 팀을 구성하게 될 사람들의 정보가 들어있다.

 

당연하게도 개인정보가 담겨있기 때문에 접어둔 상태다.

 

Store를 이용해 만들었지만, `return`에 넣지 않음으로써 외부에서 접근하지 못하도록 만드셨다.

 

const teams = ref(loadTeams());

 

그와 반대로, 실제 생성될 `teams`는 `ref`로 감싸서 반응형 상태를 부여하셨고, `return`에 넣어줌으로써 값을 전달하게 만드셨다.

 

초기 값은 `localStorage`에서 불러오는 것처럼 보인다.

 

const loadTeams = () => {
  const saved = localStorage.getItem(STORAGE_KEY);
  return saved ? JSON.parse(saved) : [];
};

const saveTeams = () => {
  localStorage.setItem(STORAGE_KEY, JSON.stringify(teams.value));
};

 

 

`loadTeams`에서는 저장된 팀을 불러오고 `saveTeams`에서는 현재 팀 상태를 저장하는 것 같다.

 

이때 해당 값들을 JSON 형태로 처리하는 것을 볼 수 있다.

 

`JSON.stringify`로 객체/배열을 문자열(JSON)으로 변환한다.

 

`JSON.parse`로 문자열(JSON)을 객체/배열로 변환한다.

 

앞으로도 JSON 형태를 많이 사용할 것이기 때문에 이렇게 만드신 것 같다.

 

  const shuffle = (array) => {
    const result = [...array];
    for (let i = result.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [result[i], result[j]] = [result[j], result[i]];
    }
    return result;
  };

 

셔플을 하는 로직은 조금 재밌는데, 배열을 랜덤으로 섞는 방식으로 "Fisher-Yates" 알고리즘을 사용하셨다.

 

그리고 원본 배열을 건드리지 않고, 값을 복사해서 사용하셨다. 

 

해당 셔플 알고리즘이 궁금하다면 위키피디아를 읽어보는 것도 좋을 것 같다.

 

  //팀 생성 (외부에서 호출) : //처리순서 : 1. 배열 섞기(shuffle) -> 2. 팀생성(teamSizes -> slice)
  const generateTeams = () => {
    // 1. 배열 섞기(shuffle)
    const shuffled = shuffle(members);
    // 2. 팀생성(teamSizes -> slice)
    const teamSizes = [5, 5, 5, 4, 4, 4];
    let index = 0;
    const result = [];
    for (const size of teamSizes) {
      result.push(shuffled.slice(index, index + size));
      index += size;
    }

    teams.value = result;
  };

 

이제 실질적으로 팀을 생성하는 코드를 읽어보자.

 

우선 배열을 셔플 알고리즘으로 적절하게 섞어준다.

 

이후 팀원 수를 적절히 부여한 뒤, 그 값 `size`로 잘라서 팀을 구성한다. 잘라낸 값을 `result`에 저장한다.

 

`teams`는 아까 전, `ref`로 반응형을 부여해줬으므로 `.value`로 값을 참조해 변경해준다.

 

return {
  teams,
  generateTeams,
  saveTeams,
};

 

마지막으로 이 Store에서 사용할 상태 데이터와 메소드들을 외부로 보내는 것으로 끝내게 된다.

 

죽음의 이지선다

 

그런데 저건 말 그대로 하나의 Pinia 스토어에 불과하다.

 

강사님은 저 로직을 구현하고, 해당 기능이 화면에 적절히 출력되도록 Vue 를 이용해서 구현하셨다.

 

오직 제비뽑기 30초를 위해서.......

 

강사님의 광기가 느껴지는 순간이었다.

 

 

강사님이 저 제비뽑기를 구현한 것처럼, 우리들도 Vue를 이용해서 가계부 앱을 만들어야 한다.

 

그렇게 조 편성이 끝났는데, 놀랍게도 21회차의 기자단 세 명이 한 팀에 모이게되었다.

이거 진짜에요?

 

엄청난 우연으로 셋이 같은 프로젝트를 진행하게 되었다. 

 

다른 포스팅에서 다들 비슷한 자료로 글을 올리지 않을까.

 

 

프로젝트 기획하기

우선 가계부 앱을 만들기 위해서 제공받은 프로젝트 상세를 바탕으로 해야할 일을 정리하기 시작했다.

 

기능 명세만 가지고 프로젝트를 진행할 수는 없기도 하고, 각자의 역할을 나누어서 개발에 들어가게 되었다.

 

나는 전반적인 거래 내역 컴포넌트와 그걸 보여줄 차트 컴포넌트를 제작하게 되었다.

 

물론 역할을 나눈다고 그것만 하게 되지는 않았지만, 굵직한 틀을 맡는다는 느낌으로 이야기를 나눴다.

브레인스토밍

 

대략적으로 역할을 나눈 후에, 어떤 타겟층을 노려야 하는가에 대해 이야기를 나눴다.

 

사실 나는 가계부를 한 번도 쓴 적이 없다. 가계부는 별로 재미가 없다.

 

내가 내 돈을 쓰겠다는데 돈 쓴다고 뭐라고 하기도 하고.

 

내가 왜 가계부를 쓰지 않을까를 되돌아보면서, 가계부라는게 사용자 공감을 끌어내기 어렵다는 문제를 발견했다.

 

그 문제에 착안해서, 조금 재밌게 유쾌한 방향을 가기로 했다.

 

그래서 우리들의 타겟팅은 우리와 같은 사회 초년생들, 다시 말해 그지들을 위한 앱을 만들자고 결정했다.

 

가난하고 배고픔

 

그래서 우리의 어플 이름은 거지 탈출이 되었다.

 

아이디어는 과거 인기를 끌었던 '거지키우기' 에서 영감을 받았다.

 

캐릭터를 키우고 목표를 달성하는 게임화 요소를 가계부에 넣어보면 어떨까! 하는 생각에 테마를 정했다.

 

 

화면 및 기능 설계

기획 배경을 세웠으니 전반적인 디자인을 결정해야 했다.

 

디자인은 전반적인 UI에 대한 CSS 코드를 작성해서 컬러 팔레트 형식으로 설정하게 되었다.

 

컬러 팔레트

 

위와 같은 컬러 팔레트를 구현해 놓고, 동일한 스타일을 유지하면서 전반적인 통일감을 주고자 했다.

 

꽤나 색감이 이쁘고 좋았던 것 같다.

 

그리고 메인 폰트로 그지 감성을 살리기 위한 꼬물꼬물한 폰트를 찾아 사용했다.

 

 

하이어라키 Draw.io

Draw.io 로 그린 하이어라키

 

개발에 들어가기 전에 Draw.io를 이용해서 대략적인 하이어라키를 작성했다.

 

원래 디렉토리 기준으로 하이어라키를 그리고 있었는데, 그리기도 불편하고 보기도 불편했다.

 

그래서 그냥 간단하게, 페이지와 컴포넌트를 구분해서 그려버렸다.

 

 

페이지는 온보딩 페이지와 메인 페이지, 요약 페이지, 챌린지 페이지, 챌린지 추가 페이지, 마이 페이지로 나눠서 구현했다.

 

그리고 각 페이지에 필요한 컴포넌트를 넣어줘서 어떻게 구현해야 할지를 설계했다.

 

컴포넌트로 나눈 이유는 쉬운 재사용이 가능하게 설계하기 위함이었지만...

 

정작 여러번 사용한 건 캘린터와 트랜젝션 컴포넌트 뿐이었다.

 

 

Pinia를 이용해서 날짜 정보와 유저 정보를 저장하도록 설계했다.

 

부모와 자식에게 Props와 Emits으로 처리할 수도 있지만, 로그인 정보를 계속 넘겨주기엔 무리도 있고 

 

무엇보다 여러 곳에서 사용할 정보다보니 하나로 관리하는게 덜 귀찮고 편했기 때문이다.

 

 

ERD 클라우드

ERD Cloud로 설계한 DB 테이블 스키마

 

ERD 클라우드를 이용해서 데이터베이스 스키마를 설계했다.

 

유저 테이블과 거래 내역, 챌린지, 카테고리 테이블이 있고, 각각의 Primary Key와 Foreign Key 등등을 구현했다.

 

그렇게 열심히 ERD를 만들어서 사용하려고 했으나...

 

 

정작 JSON-Server을 사용하기 때문에 REST API를 사용하려면 `id` 컬럼을 반드시 사용해야 했다...

 

우리가 만들어놓은 ID 컬럼들을 사용하고 싶어도 사실상 `id` 컬럼을 강제하다보니 다 뜯어고쳐야 하나 고민을 했다.

 

그래서 강사님께 질문을 했다. 강사님의 답변으로는 더미 데이터로 `id`를 넣어주고, 실 사용할 때에만 우리가 만든 테이블 ID를 사용하는걸 추천해 주셨다. 

 

나쁘지 않은 선택인 것 같아, 곧바로 목업데이터를 해당 방식으로 변경했다.

 

프로젝트 기능 명세

대략적인 기능 명세서

 

개발에 들어가기 전에, 우리가 진짜로 구현해야 하는 기능 명세를 작성했다.

 

큼직큼직하게 구현 계획을 세우고, 커밋 컨벤션을 비롯해 대략적인 전략을 구성했다.

 

하지만 막상 프로젝트를 진행하면서 feat과 fix로 대부분의 커밋을 처리했다.

 

깃 플로우 전략은 메인과 데브를 나눠서, 데브에서 피쳐 브랜치를 분기해 사용하기로 결정했다.

 

WBS ( Work Breakdown Structure )

Projects 사용해보기

 

깃허브에서 Projects 에 대한 탬플릿으로 이런 보드 형식의 WBS를 제공한다.

 

매번 Issue 탭에서 생성하고 종료하고 하는 것이 불편하기도 해서, 이걸 사용하게 되었다.

 

닫힌 이슈 탭들도 한번에 확인이 가능하고, 어떤 인원이 해결했는지도 태그가 가능하다.

 

서브 이슈들도 트래킹이 되기 때문에 전반적인 프로젝트 관리를 할 때 WBS를 사용하게 되었다.

 

생각보다 사용감이 좋아서 다음 프로젝트를 진행할 때에도 이 기능을 사용할 생각이다.

 

스크럼

스크럼 만들기

 

그리고 프로젝트를 진행할 때, 매일 아침 프로젝트 스크럼을 진행했다.

 

사실 크게 준비한 건 없지만, 어제 뭘 했는지, 오늘 뭘 할건지, 내일 뭘 해야 하는지, 그리고 모두가 알아야 하는 이슈를 회의를 하면서 적었다.

 

모두가 현재 프로젝트에 대해 동일하게 이해하고 파악하고 있어야 하기 때문에, 충돌을 최소화하는 목적에서도 꽤나 도움이 되었다.

 

차트 컴포넌트

브라우저에서의 리포트 페이지

 

내가 메인으로 구현했던 컴포넌트는 차트 컴포넌트다.

 

`db.json`에서 `transactions` 데이터를 불러와 Chart.js 라이브러리로 구현했다.

 

Chart.js 라이브러리는 브라우저에서 데이터를 그래프로 시각화해주는 자바스크립트 라이브러리다.

 

아무래도 가계부의 특성상 자신의 지출 흐름을 볼 수 있어야 하고, 소비를 분석할 수 있는 자료가 필요하다고 생각했다.

 

카테고리별 지출을 보여주면서 줄이거나 늘일 수 있는 종류를 확인 가능하게 만들었다.

 

그리고 하루마다 나가는 지출을 선 그래프로 표현해서 실제 지출 흐름을 파악할 수 있게 만들었다.

 

 

전반적인 화면

모바일 친화적 UI

 

스켈레톤 프로젝트 기능 명세에 적힌 처럼 반응형 디자인을 구현했다.

 

반응형 디자인을 만드는 김에 모바일 친화적인 UI를 만들려고 노력했다.

 

터치할 공간과 가장 필요한 정보를 한 번에 찾을 수 있게 만들었다.

 

웹 브라우저에서는 좀 더 다양한 정보를 한 화면에 확인할 수 있게 만들었다.

 

 

시행착오와 느낀점

단 일주일의 프로젝트였지만, 꽤나 많은 시행착오가 있었다.

 

다른 팀원과 Git 충돌이 발생한다던가, 업로드가 제대로 이루어지지 않는다던가.

 

JSON-Server의 id 컬럼값 조회라던가 말이다.

 

그럴 때마다 강사님께 질문하고, 팀원과 이야기하면서 문제를 해결해야 했다.

그냥 둘 다 해

 

 

프로젝트 진행 방법론으로 Cascade ModelAgile Model을 비교하면서 설명하곤 한다.

 

하지만 내가 느낀 바로는, 그냥 둘 다 해야한다가 맞는 것 같다.

 

 

프로젝트를 진행하다보면 아무리 계획을 제대로 짜 두더라도 완벽하게 진행되지는 않는다.

 

기능 명세에서 적었던 것들이 실제로 사용되지 않는 것들도 있는 것처럼 말이다.

 

하지만 프로젝트를 진행하면서 다 같이 하나의 기준을 세워두고 시작하는 것 만으로도 꽤나 많은걸 해결할 수 있었다.

 

그리고 순간적으로 문제가 발생하는 것은, 서로 이야기를 하는 것으로 대부분 해결할 수 있었다.

 

그 때 사전에 기준이 정해져 있으면 말도 빠르게 통하고 더 빠르게 해결할 수 있었다.

 

 

그래서 굳이 저 두 방법론을 나눠서 비교하는 것보단, 둘 다 사용하는게 맞는 게 아닐까.

 

그런 생각이 드는 프로젝트 경험이었다.

 

 

그리고 첫 프로젝트 경험으로 느낀 점이 있다면...

 

프로젝트 마감일이 있다면, 앞으로는 그것의 절반 안에 과제를 끝낸다는 각오를 해야 할 것 같다.

 

생각보다 시간이 너무너무 부족했다.

 

막상 개발에 착수하면서 실시간으로 바뀌어야 하는 것들이 많기도 하고, 충돌 발생을 줄이기 위해서 개발이 늦어지기도 한다.

 

단순히 혼자서 만드는 것에 비해서 일의 능률이 인원수만큼 늘어나지는 않는 듯 한 기분. 

 

그 기분은 아마 착각이 아니다.

 

그럼에도 팀 프로젝트를 할 때, 나였으면 생각하지 못했던 것들을 볼 수 있었고, 나는 못하는 것을 다른 사람이 도와줄 수 있다는 장점도 크게 다가왔다.

 

왜 팀프로젝트를 해야만 하는지 느낄 수 있었다.

 

이제 한 번 경험해봤으니, 다음 프로젝트는 더 잘 진행할 수 있겠지.

 

KB IT's Your Life 7기