1. 버전 관리
git flow

기본적으로 개발은 develop 브랜치에서 진행하였습니다.
즉, main 브랜치에서 develop 브랜치로 분기하여 기능작업을 하였습니다.

이후 각 기능은 devlop 브랜치에서 feature 브랜치를 만들어, 여기에서 진행하였습니다.
feature 브랜치 네이밍
feature 브랜치의 이름은
feature/이름/기능명
으로 통일하였습니다.예를 들어 민기님이 로그인 기능을 만들고 저는 뉴스 피드 기능을 만들고 있다면,
- feature/MK/login
- feature/SK/news_feed
이후 1차 릴리즈가 완료 되었다면, develop을 main에다 머지하는 방식입니다.

이슈 관리
팀 프로젝트인 만큼 다른 사람이 현재 무엇을 하고 있는지 인지하는 것이 가장 중요합니다.
이를 위해 저희는 이슈를 적극적으로 활용하였습니다.
Backlog

백로그 개발해야할 기능 또는 제품에서 요구하는 기능과 우선순위를 말한다.
즉, 저희는 백로그를 대단위로 생각하기로 했습니다.
예를 들자면, 로그인 기능을 만든다고 가정해봅시다.
기능 개발의 대단위는 로그인이 될것입니다.
그리고 로그인에 필요한 기능들은 세부 feature로 나눌 수 있습니다.
feature
feature는 백로그에 명시한 기능들입니다.

예를 들어 로그인 백로그를 만들었다고 가정하고, 로그인 구현을 위한 각각에 대한 기능은 feature입니다.
- login page만들기
- 비동기 처리
- 쿠키 및 세션 처리
즉, 위와 같은 것들을 feature로 관리하여 팀원들이 현재 무엇을 하고 있는지 쉽게 파악이 가능했습니다.
2. 코드 컨벤션
컴포넌트 함수와 함수 모듈은 명확하게 분리한다.
자바스크립트는 함수를 선언하기 위해 대표적으로 함수 선언식과 함수 표현식이 존재합니다.
함수 선언식
function helloWorld() { console.log('hello world'); }
함수 표현식
const helloWorld = () => { console.log('hello world'); }
컴포넌트 또한 함수 선언식과 함수 표현식 가리지 않고 모든 방법으로 만들 수 있습니다.
하지만, 컴포넌트 안에 다른 모듈이 필요한 상황에서 이 둘의 표현식이 같다면..?
const App = () => { const hello = () => console.log('hello'); return ( <h1>hello world</h1> ) } export default App;
지금은 코드량이 적기 때문에 햇갈리진 않겠지만, 코드량이 많아지게 되면 어떠한 것이 컴포넌트인지 또는 어떤 것이 모듈인지 햇갈릴 여지가 충분합니다.
따라서, 저희는 컴포넌트의 경우 함수 선언식으로, 컴포넌트 안의 모듈의 경우 함수 표현식으로 하기로 하였습니다.
export default function App() { const hello = () => console.log('hello'); return ( <h1>hello world</h1> ) }
import 순서

위는 저희 서비스의 예시입니다.
이와 같이 코드량이 많아지고 모듈화를 하면 할 수록
import
의 수는 많아지게 될 것 입니다.그리고
import
는 우리가 어떤 모듈을 사용하고 있고 어떻게 분리했는지 파악할 수 있는 유용한 문입니다.하지만, 이
import
가 뒤죽박죽 섞여있다면..? import React, { useEffect, useState } from 'react'; import { StatusBar } from 'react-native'; import { NavigationContainer } from '@react-navigation/native'; import { PortalProvider } from '@gorhom/portal'; import { Main, HandleSchedule, OnBoarding } from 'screens'; import { theme } from 'styles/theme'; import { PopProvider } from 'context'; import SplashScreen from 'react-native-splash-screen'; import { ThemeProvider } from 'styled-components/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import AsyncStorage from '@react-native-async-storage/async-storage';
아마, 이를 읽기는 쉽지 않을 것 입니다.
프로젝트 컨벤션에 따라 다르겠지만, 저희는 모듈의 성격은 크게 4가지로 나뉘어져 있다고 생각했습니다.
- react 관련 import
- 라이브러리 관련 import
- 모듈 or 커스텀 Hook 관련 import
- 스타일 import
따라서, 위 4가지 성격을 가진 import 모듈을 순서에 따라 배치하는 것으로 정했습니다.
// react 관련 import React, { useEffect, useState, useContext, useCallback } from 'react'; // 라이브러리 import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import dayjs from 'dayjs'; // 모듈 import { FloatingButton, AgendaBox } from 'components'; import { IconPlus } from 'assets'; import { RootStackParamList, IScheduleProps } from 'types'; import { connectDB, createScheduleTable, deleteScheduleItem, getDateAndDayOfWeek, getScheduleItems, } from 'db'; import { useNotification } from 'hooks'; import { ONE_DAY, parseToSlash } from 'utils'; import { PopContext } from 'context'; // 스타일 import * as S from './style';
위와 같이 순서를 정해놓으면 조금 더 읽기 편한 코드가 작성될 것입니다.
export 는 index.ts로 간편하게 처리한다.
만약, 여러개의 컴포넌트를 import 해와야 한다고 가정해봅시다.
export default function Hello() { return <div>Hello</div>; }
export default function Good() { return <div>Good</div>; }
이를 App.js에서 불러온다고 가정해봅시다.
import Hello from './Hello.js'; import Good from './Good.js'; function App() { return ( <> <Hello/> <Good/> </> ) }
물론 이렇게 하는것이 나쁘다는 것이 아닙니다.
하지만, 10개 또는 100개의 컴포넌트를 불러와야 한다면..?
따라서 저희는 Index.js에서 한꺼번에 불러오는 것으로 정했습니다.
export {Hello} from './Hello'; export {Good} from './Good';
import {Hello, Good} from './components'; function App() { return ( <> <Hello/> <Good/> </> ) } export default App;
위와 같이 하면, 각 컴포넌트마다 import하지 않고 간편하게 import 할 수 있게 되는 것이죠..
절대경로를 사용하자.
상대경로를 사용하면 경우에 따라 다음과 같이 보기가 안좋아 지는 경향이 좀 있습니다.
import {Hello} from '../../../../../../../components/Hello'; import {Good} from '../../../../../../../utils/Good'; import {Bad} from '../../../../../../../lib/Bad'; import {Temp} from '../../../../../../../hooks/Temp';
그런데, 만약 src 기준으로 절대경로로 한다면..?
import {Hello} from 'components/Hello'; import {Good} from 'utils/Good'; import {Bad} from 'lib/Bad'; import {Temp} from 'hooks/Temp';
상대경로에 있는 매우 긴 뱀같은 녀석을 간편하게 줄여버릴 수 있죠..ㅎ
모듈은 관심도에 따라 배치한다.
코드 모듈화는 중요합니다.
또한, 그 모듈의 배치도 중요합니다.
이전에는 무조건 재사용될 모듈의 경우 utils 폴더에 무조건 배치했던 것 같습니다.
function Hello() { const sayHello = () => console.log('hello'); return ( <button onClick={sayHello}>Say Hello</button> <SayHello sayHello={sayHello} /> ) }
sayHello라는 모듈이 있을 때, 이 모듈이 다른 컴포넌트에서 사용된다고 굳이 utils 폴더로 뺄 이유가 있을까요?
개인적으로는 굳이? 인것 같습니다.
왜냐하면, 해당 모듈은 Hello라는 컴포넌트에서 사용이 되고 주 관심사는 Hello 컴포넌트에 있기 때문이죠..
그래서, 무조건 다른 곳에서 정의 하기 보다는 해당 모듈이 같은 관심사의 컴포넌트에서만 사용된다면 분리보다는 상위 컴포넌트에서 정의하는 것이 더욱 응집력있는 코드가 될 거라는 생각을 하고 있습니다.
하지만, 이 기준은 팀원의 의견이기 때문에 유동적으로 처리하면 될 것 같습니다.
언제나 절대적인 것은 없으니까요..
3. 커밋 컨벤션
로그인 기능 수정
위와 같이 그냥 글 쓰듯이 커밋을 할 수도 있습니다.
하지만, 팀 프로젝트라면 팀원이 “이 자식이 무엇을 하였구나" 와 같은 현황 파악이 가장 중요하다고 생각합니다.
따라서, 내가 한 행위에 대해 키워드를 붙이면 어떠한 의도로 커밋을 했는지 알기 쉬워집니다.
feat : 새로운 기능에 대한 커밋 fix : 버그 수정에 대한 커밋 build : 빌드 관련 파일 수정에 대한 커밋 chore : 그 외 자잘한 수정에 대한 커밋 ci : CI관련 설정 수정에 대한 커밋 docs : 문서 수정에 대한 커밋 style : 코드 스타일 혹은 포맷 등에 관한 커밋 refactor : 코드 리팩토링에 대한 커밋 test : 테스트 코드 수정에 대한 커밋
그래서 저희는 이와 같이 기준을 나누었습니다.
예를 들어 내가 로그인 버튼을 추가 했다면 다음과 같이 입력하면 됩니다.
feat: 로그인 버튼 추가