본문 바로가기
IT/JavaScript & TypeScript

[Inner Circle] 5주차~13주차 고찰 및 정리

by 저당단 2025. 7. 22.

패스트캠퍼스 INNER CIRCLE 풀스택 개발 5주차~13주차의 내용을 정리합니다.

팀 프로젝트를 하면서 고민했던 부분을 멘토링 받은 부분이 포함되었습니다.

끝난지는 한달이 다 되어가는데 그동안 배운 내용에 대해서 정리를 덜 했길래 정리합니다. (4주차까지만 정리해놓음)

 

서버 액션 내부에서 SSR 캐시 무효화를 통해 자동 리패칭 가능

서버 액션인 만큼 Next.js 자체 서버를 쓸 때 가능함

- revalidatePath를 통해 리패칭

- revalidateTag를 통해 my-tag의 캐시만 무효화

'use server'
import { revalidatePath } from 'next/cache';

export async function deletePost(id: string) {
  await db.delete(id);
  revalidatePath('/posts'); // 이 페이지 서버 캐시 무효화
}
// 서버 컴포넌트 내 fetch
await fetch('https://api.example.com/data', {
  next: { tags: ['my-tag'] }
});
'use server'
import { revalidateTag } from 'next/cache';

export async function updateData() {
  await doSomething();
  revalidateTag('my-tag');
}

 

 

Next.js의 Page.tsx(=서버 컴포넌트)에선 URL의 params 를 Promise로 받아야 함

다음은 Next.js 공식 홈페이지의 예시.

export default function Page({
  params,
  searchParams,
}: {
  params: Promise<{ slug: string }>
  searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}) {
  return <h1>My Page</h1>
}

https://nextjs.org/docs/app/api-reference/file-conventions/page

 

File-system conventions: page.js | Next.js

API reference for the page.js file.

nextjs.org

헷갈려서 헤맸던 기억이 있다.

 

 

서버 컴포넌트와 클라이언트 컴포넌트 중 어떤 것을 쓸까

클라이언트 컴포넌트가 많아지면 불필요한 hydration이 생겨서 초기 로딩 속도가 느려지지만, 반면 서버 컴포넌트는 TanStack Query를 쓸 수가 없어서 로딩 화면이나 캐싱, 리패칭 기능 등에서 조금 불편하다.

 

그래서 개발하면서 정말 고민했던 부분으로, 멘토님들의 의견을 듣고 내 나름대로 결론을 내 봤다.

SEO가 필요한 컴포넌트는 서버 컴포넌트, 그렇지 않은 컴포넌트는 클라이언트 컴포넌트를 사용해 TanStack Query로 UX 측면에서 장점을 가져오는 것이다.

 

사실상 모든 컴포넌트를 클라이언트 컴포넌트로 구성하는 극단적인 경우가 아니라면 성능에서 큰 차이는 없기 때문에, 이 정도 고려사항으로 충분할 거라고 생각했다.

 

하지만 또 다른 고려사항이 있는데, 서버 컴포넌트를 사용한다면 JS 번들 사이즈가 감소한다는 점이다.

서버 컴포넌트를 많이 사용하는 것이 요즘 Next.js의 철학이란 말이 괜히 있는 게 아닐 터였고...

결론은 서버 컴포넌트와 클라이언트 컴포넌트를 고민할 때쯤이면, 그때가 컴포넌트를 분리할 타이밍인 것이다.

 

 

적은 코드로 에러 핸들링을 얼마나 잘하는지가 FE개발에서 제일 중요하다

일반적인 방법은 try/catch.
하지만 에러를 전파시켜 공통 에러 처리 컴포넌트나 바운더리를 만드는 것이 하는 게 더 나을 수 있다.

또한 Result 패턴을 통해 함수의 리턴값을 감싸는 것도 일관성에 좋은 방법이다.

function toResult<T>(fn: () => T): Result<T> {
  try {
    return { ok: true, value: fn() };
  } catch (e) {
    return { ok: false, error: e };
  }
}
const result = toResult(() => doSomethingDangerous());

 

 

이 방식의 장점은 호출하는 쪽에서 일일이 try/catch를 안 하고 ok 값으로만 구분할 수 있다는 것이다.

 

 

여러가지 유용(?)한 Web API들

https://developer.mozilla.org/en-US/docs/Web/API

 

Web APIs | MDN

When writing code for the Web, there are a large number of Web APIs available. Below is a list of all the APIs and interfaces (object types) that you may be able to use while developing your Web app or site.

developer.mozilla.org

  • Intersection Observer API - 스크롤 이벤트에 쓰임
  • Web Speech API - 말을 텍스트로 바꾸거나 그 반대로 함
  • WebRTC API - 브라우저 간에 실시간 오디오 / 비디오 통신(Zoom 같은거)
  • Web Workers / Service Workers -  백그라운드에서 병렬 작업 가능하게 해주는 거
  • Gamepad API - 게임 컨트롤러 입력을 브라우저에서 받을 수 있음
  • Web Bluetooth / Web USB / Web Serial - 브라우저에서 물리 디바이스랑 연결
  • Device Orientation / Motion API - 폰 흔들면 웹 페이지에서 반응함
  • Clipboard API - 복사 / 붙여넣기 제어하는 기능 (클립보드에 복사되었습니다 문구 표시)
  • File System Access API - 로컬 파일 시스템에 접근할 수 있음
  • Battery Status API - 사용자의 배터리 정보 얻기 (ㅋㅋㅋ)

 

 

새로운 걸 공부할 때의 팁

어떤 프레임워크가 내세우는 기능 위주로 보기

nano, tiny, mini, minimal, pico 등의 이름이 들어간 패키지 (예시 아래) 를 찾아서 보기. 간단해서 이해하기 쉽다고 한다.
https://www.jsdelivr.com/package/npm/clipboard-mini

 

jsDelivr - A free, fast, and reliable CDN for JS and Open Source

Optimized for JS and ESM delivery from npm and GitHub. Works with all web formats. Serving more than 150 billion requests per month.

www.jsdelivr.com

`awesome [언어, 프레임워크 등등] site:github.com` 으로 구글에 검색 -> 그 깃헙의 앱들을 최근꺼위주로 찾아보면 공부에 도움이 된다.

 

 

DTO는 이렇게 정의하기도 한다

// 🧱 Entity: 진짜 Order 타입 (백엔드 DB 모델 매칭)
export type OrderEntity = {
  id: string
  userId: string
  status: 'pending' | 'complete' | 'cancelled'
  createdAt: string
  updatedAt: string
}

ts
// 🧾 Form용: 사용자가 작성하는 부분만
export type OrderFormValues = Pick<OrderEntity, 'userId'> & {
  // 만약 폼에서 상태도 설정하게 하고 싶으면
  status?: OrderEntity['status']
}

ts
// 📬 API 요청용 DTO: 생성할 때 필요한 값만
export type CreateOrderRequest = Pick<OrderEntity, 'userId' | 'status'>

// 📩 API 응답용 DTO: 일부 필드만 내려주거나 가공해서 보냄
export type OrderResponse = Omit<OrderEntity, 'updatedAt'> & {
  totalPrice: number
  items: OrderItemResponse[]
}

 

모델에 맞추어 정의한 방식.

 

 

DDD (도메인 주도 개발) 코드 예시

class Order {
  private status: 'pending' | 'complete' | 'cancelled'

  constructor(
    private readonly userId: string,
    private items: OrderItem[],
  ) {
    this.status = 'pending'
  }

  complete() {
    if (this.status !== 'pending') {
      throw new Error('이미 완료된 주문은 완료할 수 없음')
    }
    this.status = 'complete'
  }

  cancel() {
    if (this.status === 'complete') {
      throw new Error('완료된 주문은 취소할 수 없음')
    }
    this.status = 'cancelled'
  }

  getTotalPrice(): number {
    return this.items.reduce((sum, item) => sum + item.price, 0)
  }

  // 기타 도메인 로직
}

 

 

ISR vs Streaming HTML

- ISR(Incremental Static Regeneration)은 정적 페이지를 백그라운드에서 갱신함. 페이지 단위로 캐시하는 것이 특징.

- Streaming HTML은 서버에서 HTML을 점진적으로 전송하는 기술. 컴포넌트 단위임

 

ISR은 정적 페이지를 위한 기술인 만큼 getStaticProps 내부에서 쓰이는 기술이었으나 현재의 App Router 기반 Next.js 트렌드에서는 쓰이지 않는다.

Streaming HTML은 기본적으로 Next.js에서 자동으로 작동시키지만 Suspense를 써야 컴포넌트 단위로 점진적으로 보이며, 안 쓰면 가져오는건 점진적이지만 UI 렌더링은 한 번에 이루어진다.

 

 

포폴 작성 팁

'어떤 기술을 썼다'는 것은 누구나 할 수 있는 것이기 때문에 스펙이 될 수 없다.
next.js를 썼다 -> x
next.js를 썼는데 무슨 문제가 있어서 그걸 해결했다 -> o
next.js를 다른거랑 비교해봤는데 이게 좋아서 썼다 -> o