프론트엔드 기술면접 대비 서비스 "프터뷰" 회고🎤

홍다희·2023년 2월 8일
0
post-thumbnail

프로젝트 소개

프터뷰

퀴즈를 풀면서 프론트엔드 기술면접을 대비할 수 있는 앱, 프터뷰를 개발하게 됐다.
회원가입, 로그인, 랭킹, 오답노트(틀린 문제들을 볼 수 있다), 퀴즈 등의 기능이 있고, 기술면접 관련 지식들을 사지선다형 퀴즈로 풀면서 자연스럽게 키워드들을 외울 수 있다.

평소에 앱 개발에 관심이 있었기 때문에 리액트 네이티브를 이용해 처음으로 앱 개발을 할 수 있는 좋은 경험이었다.

함께 작업한 종열님께선 풀스택을 맡으셔서 node.js로 서버를 구성하셨고, 난 프론트를 맡았는데 프론트에선 React native, Typescript, React-Query 등을 사용했다.

고민했던 것들

1. MVVM 패턴

폴더구조도 중요하니까 MVVM 패턴을 적용해 보는 건 어떻겠냐는 종열님 제안에 MVVM 패턴을 적용하게 됐다. 처음 적용해 봐서 좀 어려웠지만 적응되고 나니깐 쓰기 편했다. 보여지는 화면(View), 데이터 패칭(Model), View에서 사용하는 상태 및 데이터 가공(ViewModel)으로 코드가 분리돼서 가독성이 올라갔다고 느꼈다.

그런데 리액트 쿼리를 사용하면서부터 문제가 생겼었다. 리액트 쿼리 함수를 model에서 사용하면 invalid hook call 에러가 발생하는 문제였다. 결국 view로 옮겨서 사용했더니 잘 작동해서 프로젝트 진행을 위해 폴더구조는 그대로 두고, 리액트 쿼리 함수만 view로 옮겨서 사용하게 되었다. 완벽하게 적용하진 못했지만 MVVM 패턴이 뭔지 이해할 수 있었다.

2. Stack, Bottom tab navigator

프터뷰 앱은 하단 탭이 필요했기 때문에 라우팅을 할 때 bottom tab navigator가 필요했다. 그리고 탭 말고도 버튼을 눌러서 이동하는 페이지도 있었기 때문에 stack navigator 또한 필요했다. 그래서 둘을 결합해야 했는데 이 과정이 좀 어려웠다.
처음엔 Tab 안에 Stack을 넣는 구조로 진행했다.

const Stack = createNativeStackNavigator();
const StackNavigator = () => {
  return (
    <Stack.Navigator
      initialRouteName="Main"
      screenOptions={{ headerShown: false }}
    >
      <Stack.Screen name="Main" component={Main} />
      <Stack.Screen name="Questions" component={Questions} />
      <Stack.Screen name="MyPage" component={MyPage} />
    </Stack.Navigator>
  );
};

이렇게 stack navigator를 만들고

const Tab = createBottomTabNavigator();

const TabBar = () => {
  return (
    <NavigationContainer>
      <Tab.Navigator
        screenOptions={{
          tabBarInactiveBackgroundColor: "white",
          tabBarActiveTintColor: "#ff8dc7",
          tabBarInactiveTintColor: "black",
          headerShown: false,
        }}
      >
        <Tab.Screen
          name="홈"
          component={StackNavigator}
          options={{
            tabBarIcon: () => <MaterialCommunityIcons name="home" size={23} />,
          }}
        />
        <Tab.Screen
          name="오답노트"
          component={Note}
          options={{
            tabBarIcon: () => (
              <MaterialCommunityIcons name="notebook" size={23} />
            ),
          }}
        />
        <Tab.Screen
          name="랭킹"
          component={Ranking}
          options={{
            tabBarIcon: () => (
              <MaterialCommunityIcons name="trophy" size={23} />
            ),
          }}
        />
        <Tab.Screen
          name="나의 프터뷰"
          component={MyPage}
          options={{
            tabBarIcon: () => (
              <MaterialCommunityIcons name="human-greeting-variant" size={23} />
            ),
          }}
        />
      </Tab.Navigator>
    </NavigationContainer>
  );
};

tab의 홈 스크린에 StackNavigator를 넣어서 결합했다.
이 방식으로도 잘 작동은 했지만, 홈에서 버튼을 눌러 퀴즈 화면이나 프로필 화면으로 이동할 때(Stack Navigator로 이동) 사용자들에게 여전히 하단 탭에서 홈이 활성화 돼 있는 걸로 보인다. 또한 홈 화면에서 퀴즈 화면으로 이동하고 다른 탭( ex) 마이페이지)로 이동했다가, 다시 홈 화면으로 돌아가면 그대로 퀴즈 화면이 떠 있는 문제가 있었다.

그래서 라우팅이 자연스럽지 않다고 느껴서 처음에 한 방법과 반대로 StackNavigator 안에 Bottom Tab navigator를 넣는 방법으로 바꾸게 되었다.

const Tab = createBottomTabNavigator();

export const TabBar = () => {
  return (
    <Tab.Navigator
      screenOptions={{
        tabBarInactiveBackgroundColor: "white",
        tabBarActiveTintColor: "#ff8dc7",
        tabBarInactiveTintColor: "black",
        headerShown: false,
      }}
    >
      <Tab.Screen
        name="홈"
        component={Main}
        options={{
          tabBarIcon: () => <MaterialCommunityIcons name="home" size={23} />,
        }}
      />
      <Tab.Screen
        name="오답노트"
        component={Note}
        options={{
          tabBarIcon: () => (
            <MaterialCommunityIcons name="notebook" size={23} />
          ),
        }}
      />
      <Tab.Screen
        name="랭킹"
        component={Ranking}
        options={{
          tabBarIcon: () => <MaterialCommunityIcons name="trophy" size={23} />,
        }}
      />
      <Tab.Screen
        name="나의 프터뷰"
        component={MyPage}
        options={{
          tabBarIcon: () => (
            <MaterialCommunityIcons name="human-greeting-variant" size={23} />
          ),
        }}
      />
      <Tab.Screen
        name="결과창"
        component={Result}
        options={{
          tabBarIcon: () => (
            <MaterialCommunityIcons name="human-greeting-variant" size={23} />
          ),
        }}
      />
    </Tab.Navigator>
  );
};

변경 후의 Bottom Tab Navigator.

const Stack = createNativeStackNavigator();

export const StackNavigator = () => {
  const { theme } = DarkModeViewModel();

  return (
    <ThemeProvider theme={theme ? darkTheme : lightTheme}>
      <NavigationContainer>
        <Stack.Navigator
          initialRouteName="Main"
          screenOptions={{ headerShown: false }}
        >
          <Stack.Screen name="Main" component={TabBar} />
          <Stack.Screen name="Questions" component={Questions} />
          <Stack.Screen name="MyPage" component={MyPage} />
          <Stack.Screen name="Result" component={Result} />
          <Stack.Screen name="Login" component={Login} />
          <Stack.Screen name="SignUp" component={SignUp} />
        </Stack.Navigator>
      </NavigationContainer>
    </ThemeProvider>
  );
};

변경 후의 Stack Navigator.

변경 후엔 Stack Navigator를 통해 이동했을 때(ex) 문제 페이지, 로그인 페이지) 탭은 사라지고 이동한 페이지 화면만 뜨기 때문에 라우팅이 훨씬 자연스럽다고 느꼈다.

탭이 있는 화면들과


탭이 사라지는 문제 페이지 화면.

navigator들을 결합하는 또다른 방법도 있다. 웹보다 라우팅이 복잡한 거 같다.

느낀 점

앱 개발을 맛 볼 수 있어서 좋았다! 한 가지 아쉬운 건 배포를 못 한 게 아쉬웠다. 개발이 끝나고 마지막에 배포를 시도했는데 안드로이드에선 실행이 안 되는 오류가 있었다...😭 항상 ios로만 테스트를 했었기에 이런 문제가 생길 줄 몰랐다. 그래서 앱 개발을 할 땐 ios, 안드로이드 둘다 테스트를 하면서 개발해야 한단 걸 뒤늦게 깨달았다. 그리고 배포는 프로젝트 중간에 해 보는 것도 중요한 거 같다. 다음엔 이런 아쉬웠던 점들을 보충해서 스토어에 출시도 할 수 있었으면 좋겠다. 그리고 풀스택으로 3인분 이상을 해 주신 종열님 감사합니다!👍🏻👍🏻👍🏻

profile
프론트엔드 개발자

0개의 댓글