여행 어플 oeoTravel 리팩토링

jimmy neutron·2023년 2월 15일
0

react native

목록 보기
2/2

리팩토링 계기

이 프로젝트를 진행한지 벌써 8개월이라는 시간이 지났고 그 동안 개인적으로 많은 성장이 있었다. 또한 8개월 전의 나와 지금의 나는 얼마나 다른 방식으로 코드를 바라보는지 어떤 부분을 중점적으로 코드를 작성하는지 등 개발자로서 성장하기 위해 꼭 되짚어봐야할 부분들이 많다고 생각했다.

그리고 만약 회사에서 레거시 코드를 마주했을 때 어떻게 대처해야할지 작게나마 미리 체험해볼 수 있을거같아서 전체적인 기존 코드 리팩토링을 하기로 결심했다. 전체 코드 리팩토링 후 몇 가지 새로운 기능도 추가할 예정이다.

목차

  1. 디렉토리 구조
  2. 상태관리
  3. 함수 관심사 분리
  4. 그 외
  5. 1차 리팩토링 최종 구조

디렉토리 구조(진행 중)

oeoTravel
├─ App.js
├─ app.json
├─ assets
│  ├─ adaptive-icon.png
│  ├─ favicon.png
│  ├─ icon.png
│  ├─ splash.png
│  └─ ticket
│     ├─ hotel
│     │  ├─ 7-16.jpeg
│     ├─ plane
│     │  ├─ gTr.jpeg
│     └─ train
│        ├─ frankToUlm.png
└─ src
   ├─ component
   │  ├─ common
   │  │  ├─ header.js
   │  │  └─ modal.js
   │  ├─ context
   │  │  └─ mapContext.js
   │  ├─ home
   │  │  ├─ homeScreen.js
   │  └─ map
   │     ├─ customs.js
   ├─ constant
   │  └─ constants.js
   └─ mock
      ├─ buttons.js

컴포넌트 디렉토리 안에서 home과 map 페이지별로 나눈거같은데 이렇게 되면 재사용 가능한 컴포넌트를 따로 분리해줄 디렉토리가 없어진다.

따라서 우선 pages라는 디렉토리를 만들고 Home.jsx페이지와 Map.jsx페이지로 나누고 각 페이지에서 사용하는 컴포넌트들을 component디렉토리에 분리해줘야한다.

└─ src
   ├─ component
   │  ├─ home
   │  ├─ map
   └─ pages
   │  ├─ Home.jsx
   │  │─ Map.jsx

컴포넌트 디렉토리에 있는 homemap 디렉토리는 각 페이지에서 사용하는 작은 컴포넌트들을 넣을 예정이다.

상태관리(진행 예정)

나머지 디렉토리를 살펴보니 constants, context, mock 이 있었는데 각각 상수, context api관련 코드, 목데이터가 들어있었다. 먼저 context를 살펴보았는데

import { useState, createContext } from "react";
import { maxCoor } from "../mock/latLng";

const MapContext = createContext(null);

function MapInfoContext(props) {
  const [modalVisible, setModalVisible] = useState(false);
  const [fitCoor, setFitCoor] = useState(maxCoor);
  const [isPress, setIsPress] = useState(false);
  const [modalId, setModalId] = useState("");

  const mapInfo = {
    modalVisible,
    setModalVisible,
    fitCoor,
    setFitCoor,
    isPress,
    setIsPress,
    modalId,
    setModalId,
  };
  return (
    <MapContext.Provider value={mapInfo}>{props.children}</MapContext.Provider>
  );
}

export { MapContext, MapInfoContext };

이렇게 관심사 분리 없이 한곳에 모든 state와 action들이 뭉쳐있었다.
상태관리 라이브러리를 사용하기보단 context api와 useReducer를 결합해서 간단하게 정리하고자 했다.(예정)

함수 관심사 분리(80% 완)

다행히 한 컴포넌트에서 여러개의 기능을 수행하는 경우는 없었지만 여러개의 컴포넌트가 부모 컴포넌트에 뭉쳐있는 경우가 있었다.

import { Button } from "@rneui/themed";
import Styled from "styled-components/native";
import { View, Text } from "react-native";
import { Ionicons } from "@expo/vector-icons";

export function CustomMarker({ dayCount }) {
  return (
    <CustomMarkerWrapper>
      <TextWrapper>
        <Text
          style={{
            fontWeight: "700",
            fontSize: 11,
            color: "white",
          }}
        >
          {` day${dayCount}`}
        </Text>
        <Ionicons name="chevron-forward-outline" size={14} color={"white"} />
      </TextWrapper>
      <Ionicons name="flag-outline" size={27} />
    </CustomMarkerWrapper>
  );
}

export function CustomCallout() {
  return (
    <View style={{ backgroundColor: "transparent" }}>
      <Items title={"상세 보기"} />
    </View>
  );
}

export function Items({ onPressHandler, title }) {
  return (
    <Button
      title={<ItemCard title={title} />}
      onPress={onPressHandler}
      titleStyle={{ fontWeight: "700", fontSize: 5 }}
      buttonStyle={{
        borderWidth: 0,
        borderColor: "transparent",
        borderRadius: 10,
        backgroundColor: "#100f0f",
        width: 135,
      }}
      containerStyle={{
        width: "auto",
        marginVertical: 2,
      }}
      icon={{
        name: "angle-right",
        type: "font-awesome",
        size: 20,
        color: "white",
      }}
      iconRight
      iconContainerStyle={{ marginLeft: 30 }}
    />
  );
}

function ItemCard({ title }) {
  return (
    <View style={{ flexDirection: "column" }}>
      <Text
        style={{
          fontWeight: "bold",
          fontStyle: "italic",
          fontSize: 15,
          color: "white",
        }}
      >
        {title}
      </Text>
    </View>
  );
}

여러가지 기능을 한곳에서 수행하는 컴포넌트는 아니지만
ItemCard컴포넌트를 다른 곳에서 재사용하고 있었기 때문에 분리해줬다.

그리고 같은 함수를 같은 구조로 여러번 호출하는 코드가 있었다.

// Hotels.jsx
const hotelDate = [
  { date: "7/4", ticket: [require("../../../assets/ticket/hotel/7-4.jpeg")] },
  { date: "7/6", ticket: [require("../../../assets/ticket/hotel/7-6.jpeg")] },
  { date: "7/7", ticket: [require("../../../assets/ticket/hotel/7-7.jpeg")] },
  { date: "7/8", ticket: [require("../../../assets/ticket/hotel/7-7.jpeg")] }
];

export default function Hotels({ navigation }) {
  return <SelectDate dates={hotelDate} navigation={navigation} />;
}

// Plane.jsx
const planeDate = [
  {
    date: "7/7",
    ticket: [
      require("../../../assets/ticket/plane/mTp1.png"),
      require("../../../assets/ticket/plane/mTp1.png"),
    ],
  },
  {
    date: "7/13",
    ticket: [require("../../../assets/ticket/plane/gTr.jpeg")],
  },
];

export default function Planes({ navigation }) {
  return <SelectDate dates={planeDate} navigation={navigation} />;
}

date데이터만 다르고 구조가 같은 컴포넌트가 4개나 존재했기 때문에 이를 하나의 유틸함수로 분리하고 데이터를 props로 받게 변경했다. 그리고 각 데이터는 mock 데이터에 따로 분류해서 import 해 사용했다.

// selectDate 컴포넌트
import { hotelDate, planeDate, ticketDate, trainDate } from "../mock/docData";

<SelectDate {...props} dates={hotelDate} />

그 외

각 디렉토리의 파일들을 index에서 한번에 export 해줬다.

export { default as DefaultHome } from "./DefaultHome";
export { default as TicketDetail } from "./TicketDetail";
export { default as SelectDate } from "./SelectDate/SelectDate";

1차 리팩토링 최종 구조

└─ src
   ├─ component
   │  ├─ Header.jsx
   │  ├─ Modal.jsx
   │  ├─ home
   │  │  ├─ DefaultHome.jsx
   │  │  ├─ SelectDate
   │  │  │  ├─ DateCardTitle.jsx
   │  │  │  └─ SelectDate.jsx
   │  │  ├─ TicketDetail.jsx
   │  │  └─ index.js
   │  ├─ index.js
   │  └─ map
   │     ├─ Markers.jsx
   │     ├─ customs
   │     │  ├─ CustomCallout.jsx
   │     │  ├─ CustomMarker.jsx
   │     │  ├─ ItemCard.jsx
   │     │  ├─ Items.jsx
   │     │  └─ index.js
   │     └─ index.js
   ├─ constants
   │  └─ constants.js
   ├─ context
   │  └─ mapContext.js
   ├─ mock
   │  ├─ docData.js
   │  ├─ latLng.js
   │  └─ schedule.js
   └─ pages
      ├─ Home.jsx
      ├─ Map.jsx
      └─ index.js
profile
프론트엔드로 지구정복

0개의 댓글