2025.11.11

TIL

✅ 오늘 한 일

자꾸 헷갈리는 번들과 빌드의 차이
webpack은 번들러일까, 빌드할 때 쓰는 도구인가 ?
turbopack은 번들러일까, 빌드할 때 쓰는 도구인가 ?

  1. 번들러
    기존 문제점
  • 전역 범위를 갖는 수백 개의 자바스크립트 파일의 중복 선언
  • 수백 개에 달하는 자바스크립트 파일로 인한 느린 로딩
  • 수동적인 웹 개발 루틴 (파일, 이미지 압축 / CSS 전처리기 변환 등)

웹 애플리케이션을 구성하는 모든 자원을 하나의 파일로 묶는 도구.

  • 모듈 단위의 코드 작성
  • 네트워크 병목 완화 (최적화)
  • 웹 개발 작업 자동화

웹팩은 번들러임.
하나의 웹 서비스를 구성하는 파일들을 해석한 다음 하나의 파일로 합쳐주는 역할을 함.

그러니까 웹팩이 하는 역할인 번들링은 빌드의 한 작업 단계인 거지.


  1. 빌드

개발 코드를 실제 배포 가능한 형태로 만드는 전체 과정

여러 작업들의 집합임.
예를 들면,

  • TypeScript -> JavaScript 트랜스파일
  • SCSS -> CSS 전처리
  • import/export 구조를 하나로 묶는 번들링
  • 코드 난독화, 압축, 최적화
  • 이미지/폰트 등 정적 자산 복사
    이런 전체 과정을 통틀어 "빌드"라고 함.

구체적인 상황을 예를 들면
TypeScript로 작성된 여러 개의 모듈이 있을 떄,

트랜스파일 (typescript -> javascript) 가 먼저 일어나고, 그 결과물(js)를 번들러가 묶음.
트랜스파일 -> 번들링 -> 최적화 -> 출력


webpack은 번들러일까, 빌드할 때 쓰는 도구인가 ?
turbopack은 번들러일까, 빌드할 때 쓰는 도구인가 ?
를 물어보면 둘 다 맞다. 번들러이고, 번들러는 빌드할 때 쓰는 도구임.


HMS에서 next를 쓰고 버전이 14이므로 webpack을 쓰는데, 로컬에서 실행하는 속도가 너무 느려서 고민하던 중 next에서 공식 지원하는 turbopack이 빠르다고 해, 업데이트를 했다.

그런데 별 생각 없이

npm i next@latest react@latest react-dom@latest

하니까 next 16이 설치되어 있었다 ; 아오 공식 문서를 잘 읽어보자 ...ㅜ

그래서 하 이렇게 저렇게 뭐 설정하면서 돌려보는데, turbopack을 적용해보니까 아주 박살남.
아으 어지러..

일단 사전 설정은

  1. webpack 설정을 turbopack으로 옮기기 (svgr, storybook 빌드 과정에서 제외)
  2. 잃어버린 withPWA ... 이거 다시 찾아야 함. <- ❗❗❗
const nextConfig: NextConfig = {
  turbopack: {
    rules: {
      '*.svg': {
        loaders: ['@svgr/webpack'],
        as: '*.js',
      },
    },
  },

이제부터 트러블 슈팅 하면서 변경 사항 찾기

  1. Error: Route "/detail/[cardName]" used params.cardName. params is a Promise and must be unwrapped with await or React.use() before accessing its properties. Learn more: https://nextjs.org/docs/messages/sync-dynamic-apis

params 불러올 때 Promise 고, await 로 감싸줘야 함.
아래처럼

async function Page({ params }) {
  // asynchronous access of `params.id`.
  const { id } = await params
  return <p>ID: {id}</p>
}

내 코드에서 이렇게 있던 걸

const info = pageInfo[params.cardName]

이렇게 변경해주고, 선언부에 async와, 반환 타입 Promise도 적용해줌.

const info = pageInfo[(await params).cardName]
  1. ssr: false is not allowed with next/dynamic in Server Components. Please move it into a Client Component.
const RobotErrorListCard = dynamic(() => import('@/containers/dashboard-page/summary-section/RobotErrorListCard'), {
  ssr: false,
})

이거 안된다 함.

일단 {ssr: false}를 왜 붙인거냐면 , 저 컴포넌트에서 서버 사이드 렌더링을 비활성화 함.

이 옵션은 다음과 같은 이유로 사용:

  1. 브라우저 전용 API 사용
    1. window, document, navigator
    2. 지도 라이브러리
    3. 캔버스/WebGL 사용
  2. 서버에서 불가능한 컴포넌트
	// 예: window 객체 사용
	const width = window.innerWidth // 서버에서 에러
	```
3. 번들 크기 최적화
	1. 초기 로딩 시 해당 컴포넌트를 제외
	2. 클라이언트에서만 로드

next 15에서부터는 ssr:false 옵션이 deprecated 됨.
명시적으로 "use client"를 사용해야 함.

하면 일단 변경 끗.

근데 이제 페이지 이동하면서 확인해보니까 문제 발생함.
![[Pasted image 20251111182412.png|Pasted image 20251111182412.png]]
데이터 조회를 하는데 대단한 요청을 한다.

```ts
const { data: motorTempData, isLoading: motorTempIsLoading, dataUpdatedAt: motorTempUpdatedAt } = useGetMotorTemp(params.mapId, robotId, {
  startDate: params.startDate,
  endDate: params.endDate,
  period: params.startDate && params.endDate ? 'custom' : 'hour',
})
const { data: motorCurrentData, isLoading: motorCurrentIsLoading } = useGetMotorCurrent(params.mapId, robotId, {
  startDate: params.startDate,
  endDate: params.endDate,
  period: params.startDate && params.endDate ? 'custom' : 'hour',
})
const { data: motorVoltageData, isLoading: motorVoltageIsLoading } = useGetMotorVoltage(params.mapId, robotId, {
  startDate: params.startDate,
  endDate: params.endDate,
  period: params.startDate && params.endDate ? 'custom' : 'hour',
})
const { data: batteryVoltageData, isLoading: batteryVoltageIsLoading } = useGetBatteryVoltage(params.mapId, robotId, {
  startDate: params.startDate,
  endDate: params.endDate,
  period: params.startDate && params.endDate ? 'custom' : 'hour',
})

쿼리에서 매 렌더링마다 새 객체를 생성해 queryKey가 계속 바뀌는 것이 원인.
queryKey가 바뀌면 새로운 요청 발생하고, 이 루프를 무한 반복.

params 객체를 메모이제이션

const detectionParams = useMemo(
() => ({
  startDate: params.startDate,
  endDate: params.endDate,
  period: (params.startDate && params.endDate ? 'custom' : 'hour') as 'custom' | 'hour',
}),
[params.startDate, params.endDate]
)

근데 계속 요청함. 진자 원인 뭐지 ????????????????? 아마 next 업데이트 하면서 새로운 뭔가가 생긴ㄱ ㅓㅅ 같다......

--

turbo pack 사용 후기
진자 감동이다 ... 이거 왜 지금 썼지
로컬에서 돌리는데 배포한 서버에서 쓰는 것 같다

2025.09.09

TIL

오늘 할 일

  • 타이머 UI 라이브러리 찾기

    • styled-components가 deprecated 되어서 다른 툴을 사용해야 함.
    • 사용하는 프로젝트 모두 tailwind를 사용하긴 하는데, 설정이 중복되는게 괜찮나 싶음.
    • 그렇다고 다른 css 방식을 택하자니 유지보수 면에서 괜찮나 싶음.
    • 한다면 -> CSS Module 방식
      • CSS 클래스가 중첩되는 것을 방지할 수 있음.
      • 최대한 러닝 커브가 낮은 방향으로 구성하는 게 좋을 것 같다.
  • 타이머 함수들 안쪽으로 숨길 방법 찾기

<Timer slots={slots} />

이런 식으로만 slot 정보만 보내도 될 것 같다.
또 굳이 쿼리를 써야 하나?

쿼리 쓰는 이유

  • 데이터 캐싱 (같은 요청 반복 시 캐시 활용)
  • 자동 리패치 (윈도우 포커스, 네트워크 리커넥트 시)
  • 로딩/에러 상태 관리
  • 동시 요청 dedeuplicaiton
  • 쿼리 무효화 및 의존 관계 관리

근데 타이머는 요청만 보내고 그 response 값을 사용하지 않음.
응답은 SSE로 따로 들어옴. 요청 API 자체의 response payload는 의미 없다.

  1. Query의 목적은 서버 상태를 불러오고 캐싱 및 동기화하는 것인데, 여기서는 API response가 없으니 캐싱할 가치가 없다.
  2. Mutation의 목적은 서버 변경 요청 후 결과를 관리하는 것인데, 결과는 요청 response가 아니라 SSE 스트림으로 들어오기 떄문에 mutation이 주는 이점 (성공/실패 시 상태 관리, 캐시 무효화 등)이 크게 줄어든다.

결과적으로 개선할 것들

  1. 각 버튼의 요청 -> 함수 안으로 넣어서 쓰게 하기
  2. query -> 단순 axios 만 사용하도록 변경
  3. styled-component -> css module 방식으로 변경
    • 근데 이건 UI 픽스 되면 변경하는 걸로.

오늘 할 일

  • config 모노레포 -> npm 배포 -> 적용하도록
  • 각 버튼의 요청 -> 함수 안으로 넣어서 쓰게 하기
  • query -> 단순 axios 만 사용하도록 변경
✅ 오늘 한 일
  • config 모노레포 npm 배포 및 적용 테스트 완료
  • 일단 helper-fry-frontend 레포만 적용했음. 근데 commit 은 안함.
  • typescript 설정을 빼고, typescript를 사용하는 환경일 때만 해당하는 plugin 설치하도록 해야 할 듯 하다.
  • changeset 을 잘 쓸 수 있는 방법 연구 + release 버전을 github랑 연동시킬 수 있는 듯 하다. 알아보기.
    내일부턴 타이머 리팩토링 해야 할 듯.

2025.08.26

TIL

✅ 오늘 한 일

커밋 squash 하는 법