후기

9/23 구름에서 진행하는 오프라인 팀 챌린지 활동에 다녀왔다.

4주간 온라인 알고리즘 문제풀이를 진행하고 실제 판교의 구름스퀘어에서 만나 팀프로젝트 및 세미나를 진행하는 시간이였는데 일단 나는 판교를 처음가봤다

image

처음 들어가면 반겨주는 구름이(?)들

나중에 팀원분들하고 여기서 사진도 찍었다

행사는 크게 이동욱CTO님의 세미나, 팀빌딩 및 챌린지 대회를 진행하고 발표와 마무리 순으로 진행되었는데 처음 세미나가 너무 인상깊었고 좋았던거같다.

생각거리는 많았는데 몇 가지 생각나는 부분은

  1. 제품이 아니라 사용자 가치를 우선적으로 두기

    • 자기 공부를 위해 X , 선택의 결과는 소비자의 가치를 향상 시킬 수 있어야 한다 새로운 기술은 개인프로젝트에서!


  2. 말을 할 때 논리만 중요한건 아니다. 설득을 위한 표현방법, 태도, 평상시의 인식 모두 중요

    • 이 부분은 진짜 많이 공감이 갔다. 신빙성 없는 사람이라면 어떤 말을 해도 좋게 들리지 않으니까, 결국 사람과 사람의 대화인데 좋은 말하기 방법이 중요하다고 생각한다.


  3. 뽑고 싶은 사람과 이력서는 오히려 좋지 않은 환경에서 버티고 개선했던 경험

    • 대다수 스타트업은 결국 그러한 환경이니까 오히려 이런 경험을 좋게보신다는게 신기했다. 스타트업이면 오히려 마이너스일 수 있다고 생각했기때문


  4. 회사내의 프로젝트 업무나 의견충돌 해결방법 -> 서로에게 선을 넘지 않는 환경을 조성하기 (녹음 등..)

사실 그냥 듣는 내내 감탄했다. 말씀하시는 내용에 자연스럽게 몰입이되었는데 정말 개발을 사랑하고, 개발자라면 저런 태도를 가져야한다는 생각이 들었다.

글쓰기도 마찬가지로 의무감보다는 스스로의 성장과 성취감을 기준으로 나가야한다는게 조금 와닿고 방향성을 잡기에 좋았는데 확실히 남에게 보여짐을 의식하고, 틀리더라도 피드백을 확인받고자 글을 작성하는 습관을 만드는게 좋겠다고 느꼈다


image

맨 위에 질문 내가 했던건데 QnA에 올라가서 좋았다ㅎㅎ

팀 챌린지

그리고 곧 이어 바로 팀을 구성하고 팀 프로젝트를 시작했다.

일단 가만히 있으면 망할 것 같아서 무작정 기억나는분께 찾아가서 팀을 만들자고 말씀드렸고 그러다보니까 순식간에 4명이 모여서 바로 팀등록을 해버렸다.

보통 프로젝트를 하면 처음하시는분들이 오시기마련인데 어느정도 개발경험도 있으시고, 실제 현업에서 인턴활동을 하고계신분도 계셔서 운이 정말 좋았는데 백엔드 개발을 하시던 팀원분도 진짜 열심히 참여해주셔서 프론트개발자이신줄 알았다

(아닌가 애초에 다들 잘하시는분들만 온걸까??)

적극적으로 팀원분들과 의견을 나누고, 구현방향이나 역할분담을 나누었는데 최대한 충돌없이 개발할 수 있도록 이야기를 진행하고 작업을 진행했다.

image

처음 플로우를 구상할때 공책에 끄적였던 사진

지금보니까 너무 악필이네요 죄송합니다.. 어떻게 이걸로 이야기했지

정말 아쉽게도 모든 기능을 구현하지는 못했지만, 이상하게 큰 스트레스나 부담감이 좀 적었던 것 같다.

각 모듈이나 컴포넌트는 완성도 있게 나왔고, 마지막에 연결하는 과정에서 시간부족 + 실수로 인해 마무리를 못한느낌이라 오히려 적절히 테스크가 분배되고 잘 진행되었다고 생각한다.

특히 누구 하나가 막 주도하는 느낌도아니었고 모두가 동등하게 참여하고 결과물도 조금 나와서 그런것 같기도..? 뭔가 이상적인 팀프로젝트라는 느낌이 강했던것 같다.

느낀점 & 배운점

GIT CLI

처음 프로젝트를 시작하고 환경을 세팅하는데 구름에서 진행하는 행사다보니, 자연스럽게 구름 IDE를 사용해서 개발을 진행해야했다.

구름 IDE? : 웹 기반 클라우드 통합 개발 환경으로 웹사이트를 통해 동일한 환경내에서 개발이 가능

배포까지 해주는 것 같았는데 결국 Docker처럼 각 사용자 로컬 환경에 구애받지 않고 팀원 모두가 호환성이나 기타문제 없이 프로젝트를 진행할 수 있었다.

근데 문제는 구름 IDE내에서는 깃크라켄, 소스트리, 깃데스크탑과 같은 git GUI를 사용할 수 없었다.

왜냐하면 특정 컨테이너내부와 연결해야하는데 그 방법을 몰랐고 (아마 찾아보면 있었을까..? 우선 멘토님도 잘 모르시는 듯 하셨다) 있다 하더라도 찾아보고 적용하기에는 시간이 촉박했기 때문이다.

gui를 사용했던 이유는 아무래도 편리하고, 전체 깃 상태를 한 눈에 볼 수 있어서 편했으며 굳이 CLI를 사용해야한다고는 생각하지 않았었는데 막상 환경이 바뀌니까 적응하기가 쉽지 않았다.

(정말 저거 하나때문에 개발 능률 20%는 떨어진듯..)


사실 aws등의 클라우드 서비를 사용하면 위와 같으 cli기반의 명령어들을 사용해야할텐데 기본적인 부분은 정말 숙지할 필요가 있다고 생각했다.

이전에도 특정 상황에 대해서는 git Gui툴에서도 cli명령어를 사용하긴 했었는데, 정작 기본적인 pull, push, checkout과 같은 명령어는 항상 클릭 한 두번으로 처리하다보니 잘 진행하지 못했다. 외울 필요도 없었고..

cli를 사용해야하는 환경이 얼마나 많겠냐만은 회사 문화에 따라, 혹은 저러한 특수상황을 고려했을때 기본적인 부분은 알아두자고 정말 크게 다짐했다🥲

전역상태관리

프로젝트 기능 구현을 위해 몇몇 데이터를 전역적으로 관리해야할 필요가 있었는데 나는 당연하게도 팀원분들께 아래처럼 말씀을 여쭤보았다.

"저희 그럼 어떤 전역상태관리 라이브러리를 사용할까요? 간단한 Recoil이나 Zustnad?"

왜냐하면 전역상태관리 = 라이브러리(Redux, Recoil, Zustand, Mobx)를 사용해야한다고 공식처럼 생각하고있었기 때문이다.

React내부에서 Context라는 개념이 있다는건 알고는 있었지만 실제 사용해본적은 없었다.

그래서 팀원분께서 Context사용을 제안해주셨을때 조금 당황스러우면서도 곰곰히 생각해보니 그게 맞겠다는 생각이 들었다.

지금 필요한건 빠르고, 간단하게 전역데이터를 관리해 주어진 3시간내에 모든 기능을 개발해야했기때문이다.

항상 기술스택을 고르고 사용하는데 적절한 사용 근거를 생각하자고 다짐했는데 정작 습관처럼 라이브러를 외치고 있었다는게 부끄러웠다.

그리고 context내용을 잘 모르니 후반에 이슈가 발생했을때 적극적으로 문제를 해결하지 못했는데 이 부분도 너무 아쉬웠다.

그래서 기본적인 사용방법과 개념은 한번 정리해보고 넘어가고 싶었다.

context

React Context API는 React 컴포넌트 트리 안에서 전역적으로 쓸 수 있는 값을 관리하기 위해 사용한다.

특히 간단하고, 가볍다는 장점이 있는데 간단한 예시를 들어 복잡한 Context가 여러 개 사용되는 경우가 아니라면 굳이 라이브러리를 활용하지 않아도 괜찮다.

1
2
3
4
5
6
// Context가 더 복잡해진다면..?
<UserContext.Provider value={user}>
  <ThemeContext.Provider value={theme}>
    <App />
  </ThemeContext.Provider>
</UserContext.Provider>

복잡한 애플리케이션은 여러 context를 사용하는데 그 경우 복잡성이 증가할 수 있다.

또한 부모 컴포넌트의 업데이트로 인해 하위 컴포넌트가 모두 리렌더링되는데 이를 방지하기 위해 React.memo를 사용해야한다.

그리고 우리팀에서는 아래와 같이 context를 정의하고, 모달 내부 요소를 저장하고 있었다. (복잡하지 않았기때문에 Context를 사용하기 문제가 없다)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// App.js
export const FormContext = createContext();

function App() {
	const [form, setForm] = useState({
		// 1번
		name: '이름',
		phone: '01012345678',
		{ ... }
		freeMessage: '파이팅~~',
	});
	return (
		<FormContext.Provider value=>
			<div className={styles.App}>
				<Header />
				<main className={styles.main}>
					<EmptyView />
				</main>
			</div>
		</FormContext.Provider>
	);
  1. export const FormContext = createContext();

    • 새로운 context객체를 생성한다. 해당 객체를 통해 Provider 및 Consumer컴포넌트를 생성


  2. const [form, setForm] = useState()

    • 컴포넌트의 로컬상태를 정의


  1. <FormContext.Provider value=

    • FormContext에서 생성된 Provider컴포넌트를 사용해 props를 통해 Context값을 제공한다

    • App.js에서 사용하고 있기 때문에 하위의 컴포넌트들은 모두 form과 setForm에 접근할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// component
import { FormContext } from "../../App";

const EmptyView = () => {
  const { form } = useContext(FormContext);

  return (
    <Card center padding="none" className={cn(styles.emptyView)}>
      {form.name}
    </Card>
  );
};

export default EmptyView;
  1. FormContext에 접근해 form상태에 접근

그리고, 모듈화를 고려해 팀원분께서 아래처럼 코드를 개선해주셨다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { createContext, useEffect, useState } from "react";

const useModalContext = () => {
  const [form, setFormState] = useState(intialState);
  const [modalIndex, setModalIndex] = useState(0);

  const updateForm = (key, value) => {
    setFormState((prev) => {
      return {
        ...prev,
        [key]: value,
      };
    });
  };

  const updateModalIndex = (value) => {
    setModalIndex(value);
  };

  const resetForm = () => {
    setFormState(intialState);
  };

  const api = {
    form,
    updateForm,
    modalIndex,
    updateModalIndex,
    resetForm,
  };

  return api;
};

export default useModalContext;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import useModalContext from "@/components/Context/formProvider";
export const modalContext = createContext();

function App() {
  const { form, updateForm, modalIndex, updateModalIndex, resetForm } =
    useModalContext();

  return (
    <modalContext.Provider
      value={
        form,
        updateForm,
        modalIndex,
        updateModalIndex,
        resetForm,
      }
    >
      <div className={styles.App}>
        <Header />
        <main className={styles.main}>
          {users.length > 0 ? <ListView users={users} /> : <EmptyView />}
        </main>
      </div>
    </modalContext.Provider>
  );
}

export default App;
1
2
3
4
5
6
7
import { modalContext } from '@/App';

const Modal = ({ onClose, headerName }) => {
	const { form, updateForm, modalIndex, updateModalIndex, resetForm } =
		useContext(modalContext);

  1. useModalContext이라는 커스텀훅을 사용해 상태관련 로직을 모듈화 하였다

  2. useModalContext 훅은 상태와 상태 업데이트 함수들을 포함한 api 객체를 반환해 인터페이스를 명확히 하였다


근데 사실은 중간에 상태값이 업데이트되지 않는 문제가 있었다🥲

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//App.js

function App() {

	return (
		<div className={styles.App}>
			<Header />
			<main className={styles.main}>
				{users.length > 0 ? <ListView users={users} /> : <EmptyView />}
			</main>
		</div>
	);
}

import useModalContext from '../../Context/formProvider';

const Modal = ({ onClose, headerName }) => {

	const { form, updateForm, modalIndex, updateModalIndex, resetForm } =
		useModalContext();

우선 Provider를 설정하지 않았다. 컨택스트 값을 사용하는 컴포넌트는 반드시 해당 컨택스트의 Provider의 내부에 존재해야하는데 이 부분이 누락되었었다.

특정 컴포넌트에서 context에 대한 정보를 호출하면, 새로운 상태 및 함수 인스턴스가 생성되기 때문에 각각 별개의 상태를 가지게되는것이다. (값도 계속 초기화)

따라서 최상위 컴포넌트에서 전체 내용을 구독할 수 있도록 우리 프로젝트에서는 App.js에 context를 생성하고, 하위 컴포넌트에게 제공하는 형태로 수정을 진행해야했다

StoryBook

프로젝트는 스토리북을 사용해서 구현하게 되었는데, 이떄 나는 처음으로 StoryBook을 사용할 수 있었다.

사실 들어보기만 했지 직접 사용해본건 처음이였는데 왜 좋은지 정말 확실히 체감할 수 있었다고 생각한다.

첫번째로 컴포넌트를 가져다 사용할때, 어떤 Props들이 있는지 직관적으로 확인하고 이해할 수 있었다. 다른 컴포넌트들이나 라이브러리는 보통 공식문서를 보고 텍스트형태의 이해가 강요되었는데 시각적으로 보이는 공식문서느낌..?이 들었고

사실상 다른 협업자분이 사용하거나, 팀원과 협업하는데 이 StoryBook의 이점을 직접 체험할 수 있었던 것 같다.

컴포넌트 전용 문서화 느낌

컴포넌트 개별에 대한 독립적인 개발과 테스트면에서도 용이하다고 한다

결국 Prop기반의 모듈화되어있는 컴포넌트를 만들기떄문에 자연스럽게 재사용이나 확장성을 고려해서 컴포넌트를 만들수 있게 되는 장점도 있을 것 같다.

추가로 작업하면서 의문이 들었는데 제공되었던 StoryBook 컴포넌트의 버튼이나 Typograpy컴포넌트에 대해 Size, line-Hight와 같은 Props가 없어서 직접 css를 부여하는건가? 하는 궁금증이 들었다.

이 부분은 행사가 마치고 멘토님께 질문을 드렸는데 너무 명쾌하게 설명을 해주셨다

image

Luke멘토님 답변, 너무너무 감사드립니다ㅜㅜ

확실히 프로젝트내에서 big, mid, small과 같이 크기가 딱딱 맞아 떨어지고 이를 모듈화해야한다면 props에 크기에 대한 정보를 식별할 수 있도록 명칭을 부여하는게 적합하겠지만 최대한 공통으로 사용하는 컴포넌트이기때문에 가급적이면 디자인이나 기능에 영향을 미치는 속성들을 props로 받아 재사용성과 유지보수성을 향상시키는게 중요한 것 같다고 정리했다.

실제 다른 참고사이트에서도 size같은 요소를 props로 관리하고 있지는 않아보였다.

마무리

괜히 리더를 맡아서 말아먹은건 아닌가 조금 아쉽기도하지만 배워가는게 너무 많아서 즐거웠다.

다른건 몰라도 팀운은 너무 좋았다고 생각ㅎ..

저번 해커톤때는 시간이 급하니까 코드도 엉망이고, 그냥 무작정 퍼블리싱만 진행했었는데 새로운 라이브러리및 개념, 그리고 협업에 대해 다시한번 생각해보게된 좋은 계기가 되지않았나 싶다.

아 근데 Context만 잘 알았으면 수상을 노릴만했을꺼같은데 너무아쉽다ㅜㅡㅜ

image

image

아무튼 굿즈랑 사진으로 마무리~~

카테고리: ,

업데이트:

댓글남기기