Boogle-리팩토링한 것들

Sally·2026년 4월 7일

boogle-frontend 리팩토링 기록

React + TypeScript 프로젝트에서 발견한 문제점들을 고친 과정을 정리했습니다.


목차

  1. tsconfig 경로 별칭 오류 수정
  2. axios 공통 인스턴스로 통일
  3. AuthSuccess 리다이렉트 경로 오류 수정
  4. Cafe 타입 분리 및 필드명 교정

1. tsconfig 경로 별칭 오류 수정

문제

TypeScript 5.5부터 moduleResolution: "bundler" 모드에서 baseUrl이 deprecated 처리되어 빨간 줄이 생겼습니다.
또한 baseUrl 없이 paths를 사용하려면 값이 ./로 시작하는 명시적 상대경로여야 합니다.

기존 코드

// tsconfig.json
{
  "compilerOptions": {
    "moduleResolution": "bundler",

    /* Path alias */
    "baseUrl": ".",        // ← deprecated 경고
    "paths": {
      "@/*": ["src/*"]    // ← baseUrl 없으면 에러
    }
  }
}

고친 코드

// tsconfig.json
{
  "compilerOptions": {
    "moduleResolution": "bundler",

    /* Path alias */
    "paths": {
      "@/*": ["./src/*"]  // baseUrl 제거, 상대경로로 명시
    }
  }
}

핵심 정리

  • moduleResolution: "bundler" 에서는 baseUrl 없이 paths만으로 경로 별칭이 동작합니다.
  • baseUrl이 없을 때 paths 값은 반드시 ./로 시작하는 상대경로여야 합니다.

2. axios 공통 인스턴스로 통일

문제

프로젝트에 baseURLwithCredentials가 설정된 공통 axios 인스턴스(src/api/axios.ts)가 있었지만,
일부 페이지에서 이를 무시하고 axios를 직접 import해서 사용하고 있었습니다.

그 결과:

  • 서버 주소가 IP:PORT로 하드코딩됨
  • 쿠키 인증(withCredentials)이 누락됨
  • 환경변수(VITE_API_BASE_URL)가 무시됨

공통 인스턴스 (src/api/axios.ts)

const axiosInstance = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL || 'https://www.api.moonsunpower.com',
  withCredentials: true, // 쿠키 기반 인증에 필요
});

export default axiosInstance;

CafeListPage.tsx — 기존 코드

import axios from 'axios'; // ← 외부 라이브러리 직접 import

const handleCafeClick = async (cafe) => {
  const response = await axios.post('http://43.200.174.78:8080/api/cafes/save', {
  // ↑ baseURL 없음 + IP 하드코딩 + withCredentials 없음
    kakaoPlaceId: String(cafe.id),
    name: cafe.name,
  });
};

CafeListPage.tsx — 고친 코드

import api from '@/api/axios'; // ← 공통 인스턴스 import

const handleCafeClick = async (cafe) => {
  const response = await api.post('/api/cafes/save', {
  // ↑ baseURL 자동 적용 + 쿠키 자동 적용 + 환경변수 반영
    kakaoPlaceId: String(cafe.id),
    name: cafe.name,
  });
};

AddReviewPage.tsx — 기존 코드

import React, { useState } from 'react'; // ← React 미사용 import
import axios from 'axios';               // ← 직접 import

const handleSubmit = async () => {
  const response = await axios.post('/api/reviews', formData, {
    headers: { 'Content-Type': 'multipart/form-data' },
  });
};

AddReviewPage.tsx — 고친 코드

import { useState } from 'react'; // React 제거
import api from '@/api/axios';    // 공통 인스턴스 사용

const handleSubmit = async () => {
  const response = await api.post('/api/reviews', formData, {
    headers: { 'Content-Type': 'multipart/form-data' },
  });
};

핵심 정리

import axios from 'axios'import api from '@/api/axios'
baseURL없음 (전체 URL 직접 입력)자동 적용
쿠키(withCredentials)없음자동 적용
환경변수무시됨반영됨

3. AuthSuccess 리다이렉트 경로 오류 수정

문제

OAuth 로그인 후 처리하는 AuthSuccess.tsx에서 오류 발생 시 /login으로 리다이렉트하고 있었습니다.
그런데 실제 로그인 페이지 라우트는 /loginpage로 정의되어 있어 존재하지 않는 페이지로 보내는 버그였습니다.

App.tsx 라우트 정의

// 실제 로그인 페이지 경로
<Route path="/loginpage" element={<LoginPage />} />

기존 코드

// AuthSuccess.tsx
if (isNewUser === 'true') {
  navigate('/signup', { replace: true });
} else if (isNewUser === 'false') {
  navigate('/category', { replace: true });
} else {
  navigate('/login', { replace: true }); // ← 존재하지 않는 경로!
}

고친 코드

// AuthSuccess.tsx
if (isNewUser === 'true') {
  navigate('/signup', { replace: true });
} else if (isNewUser === 'false') {
  navigate('/category', { replace: true });
} else {
  navigate('/loginpage', { replace: true }); // ← 실제 경로로 수정
}

4. Cafe 타입 분리 및 필드명 교정

문제

CafeCard.tsxmap.tsx 두 파일에서 각자 Cafe라는 이름의 인터페이스를 정의하고 있었습니다.
이름은 같은데 필드 구조가 달라서 혼란스러웠고, 특히 CafeCard.tsxreview 필드는
실제로 주소(address)를 담고 있어 필드명 자체가 잘못되어 있었습니다.

기존 코드 — CafeCard.tsx

// CafeCard.tsx 파일 안에 로컬로 선언
interface Cafe {
  id: number;
  name: string;
  tags: string[];
  review: string;   // ← 실제로는 주소인데 이름이 review
  imageUrl: string;
}

기존 코드 — map.tsx

// map.tsx 파일 안에 로컬로 선언
interface Cafe {      // ← CafeCard의 Cafe와 이름은 같지만 구조가 다름
  id: number;
  name: string;
  address: string;   // ← 같은 주소인데 필드명이 다름
  latitude: number;
  longitude: number;
  thumbnail: string; // ← 같은 이미지인데 필드명이 다름 (imageUrl vs thumbnail)
}

고친 코드 — src/types/cafe.ts 신규 생성

두 타입을 역할에 맞는 이름으로 구분해 공유 파일로 분리했습니다.

// src/types/cafe.ts

// 카카오 Places API 검색 결과 (목록 화면용)
export interface KakaoCafe {
  id: string;
  name: string;
  address: string;  // review → address로 필드명 교정
  tags: string[];
  imageUrl: string;
  placeUrl: string;
  lat: number;
  lng: number;
}

// 우리 백엔드 DB에 저장된 카페 (지도 마커용)
export interface DbCafe {
  id: number;
  name: string;
  address: string;
  latitude: number;
  longitude: number;
  thumbnail: string;
}

고친 코드 — CafeCard.tsx

// 기존: 파일 안에 직접 정의
// interface Cafe { ... }  ← 삭제

// 변경: 공유 타입 import
import type { KakaoCafe } from '@/types/cafe';

interface CafeCardProps {
  cafe: KakaoCafe; // Cafe → KakaoCafe
}

// review → address
<span>{cafe.address}</span>

고친 코드 — map.tsx

// 기존: 파일 안에 직접 정의
// interface Cafe { ... }  ← 삭제

// 변경: 공유 타입 import
import type { DbCafe } from '@/types/cafe';

const [cafes, setCafes] = useState<DbCafe[]>([]); // Cafe[] → DbCafe[]

고친 코드 — CafeListPage.tsx

import type { KakaoCafe } from '@/types/cafe';

// any[] → KakaoCafe[]
const [cafes, setCafes] = useState<KakaoCafe[]>([]);

// any → KakaoCafe
const handleCafeClick = async (cafe: KakaoCafe) => { ... };

// 카카오 API 결과 매핑 시 필드명 교정
const mappedData = data.map((place) => ({
  ...
  address: place.address_name, // review: place.address_name → address로 교정
  ...
}));

핵심 정리

타입 위치각 파일 안에 따로 선언src/types/cafe.ts 한 곳에
타입 이름둘 다 Cafe로 동일KakaoCafe / DbCafe로 역할 구분
잘못된 필드명review에 주소 저장address로 교정
유지보수파일마다 수정 필요types/cafe.ts 하나만 수정
profile
sally

0개의 댓글