본 포스팅은 NomadCoders(노마드 코더)의 NextJS 강의를 기반으로 작성되었습니다.

[Client Side]

우리가 React를 사용해서 API를 호출하여 데이터를 fetch하는 작업은 모두 Client 측에서 하던 작업들이다. 일단 간단한 API 호출과 데이터를 fetch하는 React 코드를 보자.

//ReactJS Data Fetching
import {useState, useEffect} from 'react';

function Home(){
  const [isLoading, setIsLoading] = useState(true);
  const [data, setData] = useState();
  const getData = async () => {
    const response = await fetch(API_URL);
    const json = await response.json();
    setData(json);
    setLoading(false);
  };
  useEffect(() => {
    getData();
  },[]);
  return (
    <div>
      {isLoading ? "Loading" : JSON.strigify(data)}
    </div>
  )
}

export default Home;

React에서 Data를 Fetching 하는 과정을 간단한 코드로 작성해보았다. 추가적으로 Data가 완전히 Fetching 되기 전에 사용자에게 빈 화면을 보여줄 수 없기에 Loading 상태값을 만들어 Data가 호출되기 전까지는 "Loading" 문구를 띄워주는 코드까지 추가하였다.(만약 위의 코드를 NextJS 내에서 작성하였다면 파일 최상단에 Client components라는 것을 명시해주는 "use client" 명령어를 작성해 주어야 하며, 해당 페이지의 정보를 나타내는 metadata를 사용할 수 없다.)

물론 이렇게 데이터를 호출하고 사용하는데에 큰 불편함이나 문제는 없다. 데이터를 호출한 후 React-Query를 통해서 데이터 관리를 하는 방식으로 사용한다면 성능적으로 문제가 있지도 않고 편리하게 사용이 가능할 것이다. 하지만 앞서 살펴보았듯이 React로 작성한 코드는 기본적으로 CSR 방식으로 동작을 하기 때문에 Client 측 렌더링 즉, Client Components에서 지속적으로 Data를 Fetching 하는 동작이 발생할 수 밖에 없다. 이 부분은 어떻게 보면 불필요한 동작으로 여겨질 수 있다.

NextJS를 사용하면 Server Components를 사용할 수 있기 때문에 Server 측에 Fetching을 요청한 API 주소를 저장해놓을 수 있기 때문에 한 번 Data Fetching을 해놓았다면 불필요한 호출을 하지 않을 수 있다. 이 부분에 대한 내용은 다음 파트에서 NextJS에 맞는 코드와 함께 살펴보자.

[Server Side]

앞 단락에서 React를 사용한 Data Fetching 코드를 살펴보았다. 저 간단한 기능 하나에서 고려한 부분을 나열해보자.

1. 사용자를 고려하여 Data가 호출되기 전의 페이지(Loading)에 대한 부분 처리를 해주었다.
2. useEffect Hook을 사용하면서 Data를 Fetching이 페이지가 첫 렌더링 되었을 때에만 실행되도록 처리해주었다.
3. useState Hook을 사용하면서 Data를 State 값에 할당해주면서 별도로 관리를 해주며 호출이 완료 되었을 경우 Loading 문구를 사라지게 처리해주었다.
4. Data가 State에 할당이 되면서 Data Fetching이 된 이후에도 추가적인 렌더링이 발생하여 성능적인 측면에 좋지 않다.

 

VanillaJS로만 처리하던 코드를 React로 처리하면 정말 편리하고 간단하지만 위와 같이 신경 써야할 부분이 굉장히 많고 단점도 여전히 존재하는 것으로 보인다. NextJS를 통해서 Data Fetching을 하면 성능 개선뿐만 아니라 코드 또한 상당히 축약된다. 다음 코드는 NextJS를 활용한 Data Fetching 코드이다.

//NextJS Data Fetching
export const metadata = {
  title: "Home"
}
async function getData() {
  const res = await fetch(API_URL);
  const json = await res.json();
  return json;
}
async function HomePage() {
  const data = await getData(); 
  return (
    <div>
      {JSON.stringify(data)}
    </div>
  )
}

export default HomePage;

React에서 사용하던 Hook들은 하나도 필요 없고 Async/await로만 이루어져있는 코드이다. 또한 Server Components이다 보니 사용하지 못했었던 metadata도 사용할 수 있게 되었다.

코드를 살펴보면 getData라는 함수로 API 호출을 하여 Data를 받아오고 json 형태로 변환까지 해주었다.

다음으로 이 부분이 생소한데, 단순 기능을 하는 함수가 아닌 UI를 그려주는 JSX로 작성된 함수형 ComponentsAsync를 붙여준 후 내부에서 await과 getData 함수를 통해 변수에 호출한 Data 값을 할당해주었다. 이어서 그 변수를 UI로 출력해주었다.

Server Components를 사용할 수 있는 NextJS에서는 Components 자체로 Data Fetching을 한 후 직접적으로 처리가 가능하다.

[Loading Components]

가장 앞의 단락에서 작성했던 코드 예시를 살펴보면 ReactData Fetching을 하는 동안 isLoading이라는 상태값을 만들어서 유저에게 Components가 Data를 포함하고 렌더링 될 수 있도록하는 코드가 적혀있다. 잘 생각해보면 React로 프로젝트를 했던 경험을 떠올리면 특정 페이지로 넘어가는 경우에 해당 페이지에서 Data Fetching을 하는데에 일정 시간이 소요되는 경우에는 사용자가 빈 화면을 보고 있어야 하는 경우도 발생하고 로딩화면에 해당하는 Components를 별도로 생성하여 삼항 연산자를 이용해서 대기화면을 출력하는 경우도 발생했던 경우가 생각난다.

(나도 이전에 React로 한 프로젝트를 보면 Data Fetching이 발생하여도 페이지 전환 시 Navigator Bar와 페이지의 Header 부분은 미리 출력을 해놓은 상태로 만들고 싶었는데 한계에 부딪혀 하지 못했던 기억이 생생하다.)

 

하지만 NextJS에서는 정말 간단하게 Loading 화면을 구현할 수 있다. 바로 Loading 화면이 필요한 page 파일의 옆에 loading.tsx(.jsx) 파일을 생성한 후 Data Fetching이 진행되는 동안 출력하고 싶은 Components를 만들어두면 프레임워크인 NextJS가 자체적으로 Loading 화면을 생성해준다. 우리가 React에서 Loading이라는 상태값이나 별도의 Loading Components를 생성하여 사용하던 방법과 달리 page 파일과 같은 경로에 loading 파일을 생성해주면 자동적으로 Loading 화면이 생성되는 것이다.

하지만 여기서 주의해야할 점이 있다. Loading 페이지가 보여진다는 것은 메인으로 보여질 UI가 담긴 page 파일의 Component가 아직 렌더링 될 준비가 되지 않았다는 것을 의미하는 것이다. 따라서 Data Fetching이 완료되어야 렌더링이 되어여하는 Component이기 때문에 해당 Component에는 반드시 async 키워드가 붙어있어야 한다. 바로 앞 단락에서 작성했던 코드 예시의 HomePage Component와 같은 형태로 앞에 async 키워드가 붙어있어야 Loading 컴포넌트가 Data Fetching이 완료되는 시점까지 보여졌다가 메인 Components와 교체되는 것이다.

Loading 화면이 적용되는 과정을 살짝 자세하게 살펴보자.

1. 사용자가 웹 페이지에 접속을 하는 순간 NextJS는 바로 Loading 상태를 브라우저에게 전달해준다.
2. 그 과정에서 해당 페이지를 Streaming이라는 과정을 통해 작은 HTML로 분할하여 준비되어있는 Layout이나 Loading Components들을 브라우저에게 먼저 전달한다.
3. 이 과정에서 Loading Component는 서버 측에서 통신이 완료되지 않아서 기다려야한다는 신호를 브라우저에게 전달하는 것이다.
4. 메인 Component의 await가 종료되면 Loading Component는 Fetching 된 Data가 포함된 메인 Component의 UI와 교체되면서 사라지는 것이다.

 

Loading Components를 사용할 때 폴더 및 파일 구조는 다음과 같은 예시를 참고하면 된다.

loading 파일 위치

[Parallel Request]

Parallel Request병렬 요청을 말한다. 우리는 현재 NextJS를 통해서 Data Fetching에 대한 부분을 다루고 있다. NextJS의 Server 측에서 Data Fetching을 통한 최적화, 프레임워크의 장점을 활용한 Loading Components 다루기까지 알아보았다. 우리는 현재 개념을 학습중이기에 간단한 데이터 호출과 서버와의 통신이 하나인 경우를 예시로 많이 다루고 살펴보았다. 하지만 실제 사용하는 많은 웹 서비스들은 정말 많은 데이터 호출과 서버 통신이 존재한다. 심지어 한 페이지, 한 컴포넌트 내에서도 두 개 이상의 데이터 호출이 존재할 수도 있다.

우리가 앞서 살펴본 개념을 배경으로 볼 때 만약 우리가 async를 이용하여 Data Fetching을 할 때 두 개의 통신을 동시에 하는 경우 async는 순차적으로 진행이 되기 때문에 Data Fetching이 모두 완료가 되기까지 꽤나 오랜 시간이 걸리고 그에 따라 사용자가 기다리는 시간이 길어질 수도 있게 된다. 이러한 단점을 보완하기 위해 사용하는 방식이 Parallel Request이다.

Parallel Request(병렬 요청)이란 두 가지 이상의 Data Fetching을 실행할 때 데이터 통신을 병렬로 실행하여 Data를 받아오는 시간을 최소화하는 방식을 말한다.

간단히 설명하면 Data Fetching에 5초가 걸리는 async 함수 A가 있고 마찬가지로 5초가 걸리는 async 함수 B가 있다고 가정할 때, 한 Component에서 A와 B 함수 모두를 실행하여 서로 다른 데이터를 받아 사용자에게 렌더링을 해준다. 이 때 단순히 async 함수만을 통해서 실행하여 메인 Component를 렌더링하면 유저가 Loading Component를 보면서 페이지의 렌더링이 완료되는데까지 기다리는 시간이 10초가 된다. 하지만 Data Fetching을 Parallel Request를 통해 두 async 함수의 데이터를 각각 받아와 대기 시간을 5초로 줄일 수 있다고 이해하면 된다.

여기서 중요한 점은 Promise의 method인 Promis.all을 사용한다는 것이다. 위의 상황을 인지하며 아래 예시 코드를 함께 이해해보자.

async function getMovie(id:string){
  await new Promise(res => setTimeout(res,3000));
  const response = await fetch(`${API}/${id}`);
  const movie = await response.json();
  return movie;
}

async function getVideo(id: string){
  await new Promise(res => setTimeout(res,3000));
  const response = await fetch(`${API}/${id}/videos`);
  const video = await response.json();
  return video;
}

async function Movie({ params: { id } }: { params: { id: string } }) {
  const movieData = await getMovie(id);
  const videoData = await getVideo(id);
  return <h1>{movieData.title},{videoData[0].name}</h1>;
}

export default Movie;

위의 코드를 자세히 살펴보자.(예시를 위해 Promise의 응답 지연 시간을 3000ms로 설정한 코드이다.)

1. getMovie라는 async Function은 movie라는 값을 받아오는데 3000ms(=약 3초)가 소요된다.
2. getVideo라는 async Function은 video라는 값을 받아오는데 3000ms(=약 3초)가 소요된다.
3. Movie라는 메인 Component에서 위의 두 함수의 결과값을 await을 통해 받는데 Data Fetching이 순차적으로 진행되기 때문에 movie를 받는데 3초, video를 받는데 3초가 소요되서 약 6-7초가 소요된다. 아래에 통신의 결과를 보면 6.99초가 소요되었다는 것을 알 수 있다.

직렬 통신 소요 시간

브라우저를 통해서가 아닌 Console창에 Fetching 완료 시간을 출력하여 비교해보아도 쉽게 알 수 있다.

콘솔창 결과

Fethcing이 시작되고 movie를 받아오는 시간과 video를 받아오는 시간이 차이가 나는 것을 볼 수 있다.

 

하지만 Promise.all method를 통해 두 Data Fetching을 같이 한다면 어떤 결과가 나올까. 코드를 다음과 같이 작성할 수 있을 것이다.

async function getMovie(id:string){
  await new Promise(res => setTimeout(res,3000));
  const response = await fetch(`${API}/${id}`);
  const movie = await response.json();
  return movie;
}

async function getVideo(id: string){
  await new Promise(res => setTimeout(res,3000));
  const response = await fetch(`${API}/${id}/videos`);
  const video = await response.json();
  return video;
}

async function Movie({ params: { id } }: { params: { id: string } }) {
  const [movieData, videoData] = await Promise.all([getMovie(id), getVideo(id)])
  return <h1>{movieData.title},{videoData[0].name}</h1>;
}

export default Movie;

두 개의 Data Fetching을 Promis.all을 통해서 배열로 받았다. 결과는 다음과 같다.

Promise.all 소요시간

직렬 요청을 했을 때 소요 시간은 6.99s였지만 Parallel Request(병렬 요청)을 한 경우 4.10s로 40%가량 속도가 빨라진 것을 확인할 수 있다. 콘솔창에서도 결과를 확인해보자.

콘솔창 결과

아주 작은 미세한 차이가 있지만 사실상 동시에 발생한다는 것을 확인할 수 있다.

이렇게 NextJS에서는 Promise.all method를 활용하여 Parallel Request를 활용하여 Data Fetching의 최적화를 쉽게 할 수 있다는 장점이 존재한다.

[Suspense]

Suspense

우리는 React에서 Suspense라는 것을 들어보았고 사용한 경험이 있다. Suspense란 React에서 제공하는 굉장히 편리한 기능이다. 특정 Components 내에서 사용자는 웹 페이지에 접속이 완료되었지만 Data Fetching이 이루어지고 있는 경우에 사용자 입장에서 Data Fetching이 완료된 순서대로 출력되는 Waterfall 현상을 경험할 수 있게 된다. 이런 부분을 방지하기 위해 Data FetchingUI Rendering분리하여 작업하게 해주는 Component가 바로 Suspense이다. 서로 다른 시간이 소요되는 Data Fetching을 하나로 모아서 사용자에게 출력해주어 UX 측면에서 좋고 개발자 입장에서도 각기 다른 Data Fetching을 하는 Components를 분리하여 Suspense로 모아서 사용이 가능하기에 코드 유지 보수가 편리해진다는 점과 코드의 가독성이 올라간다는 긍정적인 영향을 주는 좋은 기능이다. Suspense에 관한 내용은 다음 글을 참고하여 작성하였고 한 번 읽어보는 것을 추천한다.

https://www.daleseo.com/react-suspense/

NextJS는 React의 프레임워크이기 때문에 당연히 Suspense를 제공하고 동일한 방식으로 사용이 가능하다. 앞서 우리는 Data Fetching하는 방식과 서로 다른 소요 시간을 갖는 async 함수들을 동시에 호출하는 Parallel Request에 대해서도 알아보았다.

여기서 지금까지 공부한 방식들을 차근차근 정리를 해보자면 다음과 같이 정리할 수 있다.

1. NextJS에서 Server Component에서 Data Fetcing을 하는 방법 -> UI를 그리는 함수형 Component 자체를 async 함수로 변환하며 Data Fetcing을 하는 기본 방식
2. Data Fetching이 진행되는 동안 React에서 별도의 Loading Component나 State 값을 통해 대기 화면을 생성해 UX를 관리했던 방식의 개선 -> page 파일 옆에 loading이라는 이름을 가진 파일을 생성해주면 프레임워크 자체적으로 인식하여 loading 파일의 Component를 그려주는 방식
3. 서로 다른 소요 시간을 갖는 Data Fetching이 존재하는 경우 순차적으로 진행되는 모든 Fetching이 종료될 때까지 기다리는 것이 아닌 각각의 Data Fetching을 Parallel Request(병렬 요청)으로 처리하여 소요 시간을 단축하여 성능 개선을 할 수 있는 방식.

지금까지는 Parallel Request로 모든 Data Fetching 과정을 하나로 묶어서 처리하여 각 요청의 최대 시간만큼만 기다리면 되도록 처리하였다. 하지만 여기서 발생할 수 있는 문제는 모든 호출에 동일한 시간이 소요되는 것이 아니라는 점이다. 어떤 호출은 1초, 어떤 호출은 3초가 소요된다고 가정할 때 1초만에 끝난 호출의 결과도 병렬 요청으로 인해 사용자는 3초 뒤에 모든 결과를 받아볼 수 있게 된다. 따라서 아무리 시간을 단축하더라도 방대한 데이터 호출로 인해 소요 시간이 길다면 사용자 입장에서는 불편함이 생길 수 밖에 없는 것이다. 이런 경우 사용하는 것이 Susepense이다. Suspense를 통해서 Componets의 분리 덕분에 먼저 호출이 끝난 Component에 대해서는 사용자에게 먼저 렌더링을 해줌에 따라 Data Fetcing의 최적화가 가능하다. 따라서 Parallel Request에 더하여 Suspense까지 사용한다면 Components의 분리 UI Redering 최적화 등 한 단계 업그레이드가 되는 것이다. 

사용방식은 React에서와 동일하다. 하나의 파일에 존재하는 Data Fetching Component를 별도의 파일로 따로 분리한다. 본 파일들은 UI Rendering과 Routing에 직접적인 연관이 없으므로 components 폴더에 별도로 생성하여 추후에 import 하여 사용한다. 각 파일에서 Data Fetching을 한 후 결과값을 보여주는 메인 Components에 하위 Components로 호출하여 사용하는데 각 Components를 Suspense Component로 감싸준다. 그에 이어 Loading 되는 동안 보여줄 화면을 fallback 속성으로 추가해준다. fallback 속성값은 Loading 화면에 대한 애니메이션 Components를 생성하여 넣어주어도 되고 간단한 문구로 넣어주어도 되지만 필수 속성은 아니다. 코드는 앞서 예시로 들었던 코드와 동일하여 파일만 분리한 것이기 때문에 코드 예시는 Suspense 삽입 부분만 살펴보고 폴더와 파일 구조를 위주로 살펴보도록 하자.

Suspense 사용(movies/[id]/page.tsx)
폴더 구조

Suspense를 사용하기 이전에는 Data Fetching이 완료되기 전까지 웹 페이지의 모든 UI를 보지 못하고 Loading Component를 보고 있어야 했지만 Suspense를 사용한 뒤로는 Data Fetching과 관련이 없는 요소들은 미리 렌더링되어 받아볼 수 있게 되었다. 

Parallel Request에 의한 Data Fetching에 더해 UX를 고려하여 컴포넌트 분리에 따른 Data Waterfall을 방지하는 좋은 방식이라고 정리할 수 있겠다.

[Error Handling]

Error.tsx(jsx)

우리는 앞서 NextJS의 장점을 활용한 Data Fetching의 다양한 방법과 개선해 나아가는 것에 대해 공부해보았다. 하지만 Data Fetching과 더불어 모든 웹 페이지를 구현할 때 가장 중요한 부분이 하나 있다. 바로 Error Handling(에러 처리)이다. 빠른 처리 속도, 보기 좋은 UI 등 신경써야 할 부분은 정말 많지만 무엇보다 중요한 것은 Error에 대한 부분이다.

이번 단락에서는 NextJS에서 Data Fetching을 비롯한 page에서 Error가 발생하였을 때 처리하는 방법에 대해 알아볼 예정이다. 방법은 무척 간단하다. 우리는 이전에 NextJS 프로젝트를 생성할 때 최상위 폴더인 app 폴더 내에 not-found 파일을 생성하여 오류 페이지에 대한 처리를 해준 경험이 있다. 프레임워크가 자체적으로 파일명이 page, layout, not-found, loading인 파일들을 인식한 후 해당 파일의 Components들을 알맞게 출력하고 사용하듯이 Error 페이지도 동일한 방법으로 사용된다. 특정 page 파일에서 Error가 발생했을 때 띄워줄 error.tsx(.jsx) 파일을 생성하여 해당 page 파일과 같은 경로에 넣어두면 끝이다. 그렇게 되면 프레임워크 자체적으로 해당 page 파일에서 Data Fetching이 실패했을 경우 error 파일에서 생성해둔 Component를 페이지에 띄워 사용자에게 오류를 알려주게 된다. 무척 간단하다.

하지만 역시나 주의해야할 점이 2가지가 있다.

1. 특정 page 파일에 적용될 error 파일은 해당 page 파일과 같은 폴더의 같은 경로 내에 존재해야 한다.
2. error 파일은 반드시 "use client" 명령어를 통해 Client Component라는 점을 명시해주어야 한다.

 

위 2가지 사항만 주의한다면 UI를 그려주는 page 파일의 Data Fetching 실패페이지 자체의 오류등으로 인한 Error Handling을 쉽게 다룰 수 있다.

'NextJS' 카테고리의 다른 글

[NextJS] - (2) Page Router  (2) 2024.09.10
[NextJS] - (1) NextJS  (1) 2024.09.10
[NextJS(v.14)] - (2) Routing  (2) 2024.08.16
NextJS(v.14) - Routing(1)  (4) 2024.07.06
NextJS(v.14) - Project Setup  (1) 2024.07.05
포스팅은 NomadCoders(노마드 코더)의 NextJS 강의를 기반으로 작성되었습니다.

[Automatic Routing]

page.jsx(tsx) UI 렌더링이 되는 파일이고, page.jsx(tsx) 파일을 갖고 있는 app 폴더의 하위 폴더는 NextJS의 Automatic Routing에 의해서 url 구조에 포함되게 된다.

 

라우팅이란?

웹 개발 영역에서 의미하는 라우팅이란 간단히 말해 페이지 이동을 의미한다. url에 "/login"과 같이 특정 경로를 입력했을 경우 로그인 페이지로 이동하는 과정이라고 보면 된다.

 

기존에 React를 사용해서 라우팅을 구현할 때에는 "react-router-dom"이라는 라우팅 패키지를 설치하여 원하는 라우팅 방식(ex/ BrowserRouter, HashRouter etc.)으로 컴포넌트 별로 라우팅 구조를 설정하였다.

하지만 NextJS에서는 앞서 말한 라우팅을 별도의 설치 없이 자동적으로 폴더 구조에 따라 적용을 해준다. Next13 버전으로 업데이트 되면서 root에 "app" 폴더를 생성하여 내부에 폴더를 생성하면 생성한 폴더의 이름에 맞게 자동으로 라우팅 구조가 형성된다.

예를 들어, root 폴더인 "app"의 하위에 "login"이라는 이름으로 폴더를 생성한 후 그 하위에 "page.jsx(tsx)" 파일을 생성하면 자체적으로 url에 "/login"을 입력받으면 "login" 폴더 내부의 "page.jsx(tsx)" 파일의 내부 UI를 웹 브라우저에 렌더링해준다. 

 

간단히 보면 NextJS에서 폴더 구조는 url을 생성하고, page 파일은 UI를 그려주는 역할을 한다고 봐도 무방하다.

특정 폴더를 생성하여 url을 생성하도록 했지만 그 폴더 내부에 page 파일이 없다면 NextJS는 UI를 그려주는 파일이 없기 때문에 에러 페이지를 렌더링해준다. 따라서 자동으로 라우팅 기능을 구현해주는 부분은 정말 큰 장점이지만 NextJS는 프레임워크이기 때문에 프레임워크가 요구하는 방식에 맞지 않게 폴더나 파일을 생성한다면 오류 페이지를 맞이할 수 있다는 점을 주의해야 한다.

(추가적으로 에러 페이지에 해당하는 "not-found.jsx(tsx)"라는 파일도 root 폴더에 필수로 생성을 해주어야 하는데, 이 부분은 추후에 다루도록 하겠다.)

 

우리가 url 경로를 생성하기 위해 폴더를 생성하고 그 내부에 UI 렌더링을 위한 page 파일을 생성하였지만 렌더링을 위한 파일이 아닌 별도의 기능을 하는 컴포넌트를 만들기 위해 다른 폴더와 컴포넌트 파일을 만드는 경우도 분명 있을 것이다. page 파일은 UI 렌더링이 되는 파일이라고 해서 그 내부에 페이지 내부에 있는 모든 기능에 대한 컴포넌트를 작성할 수는 없을 것이다. 별도의 컴포넌트를 생성한 후 export 한 후 page 파일에서 import 해온 후 사용하는 방식을 사용할 것이다. 그 동작을 위해 폴더를 생성한 후 page가 아닌 특정 기능에 대한 이름으로 파일을 생성했을 때에는 NextJS 자체적으로 경로나 UI 파일로 인식하지 않는다. 

예를 들어 폴더 구조가 "app/home/user - page.jsx,login - Login.jsx, Create.jsx"와 같은 구조를 띄고 있을 때 

"localhost:3000/app/home/user" 링크는 "page.jsx" 파일을 렌더링 해주겠지만, 

"localhost:3000/app/home/login" 링크는 에러 페이지를 렌더링 해 줄 것이다.

 

'NextJS' 카테고리의 다른 글

[NextJS] - (2) Page Router  (2) 2024.09.10
[NextJS] - (1) NextJS  (1) 2024.09.10
[NextJS(v.14)] - (3) Data Fetching  (0) 2024.08.20
[NextJS(v.14)] - (2) Routing  (2) 2024.08.16
NextJS(v.14) - Project Setup  (1) 2024.07.05
본 포스팅은 NomadCoders(노마드 코더)의 NextJS 강의를 기반으로 작성되었습니다.

NextJS를 시작하기 위해 프로젝트 설정을 한다.

[npm 초기화]

npm init -y

node package manager를 사용할 수 있는 초기 환경을 세팅해준다.

이 때 뒤에 -y를 붙인 이유는 모든 초기 설정에 대한 질문에 대해 ''yes''라고 응답하는 것과 같은 의미로 모든 초기 설정을 기본값으로 알아서 설정해달라는 의미이다. -y를 붙이지 않는다면 초기 설정에 대한 입력값들이 필요하다.

 

진행하면 package.json 파일이 생겨날 것이다. package.json 파일은 우리가 사용하고자 하는 npm은 서로 의존성을 갖고 패키지들이 연결되어 있기 때문에 한 패키지에서 문제가 발생하면 프로젝트 전체가 동작하지 않을 수 있는 문제가 발생할 수 있기 때문에 파일을 생성해서 관리를 할 수 있도록 도와주는 파일이다.

 

package.json 파일에 "license" 속성이 "ISC"로 설정이 되어있을텐데 그 속성값을 "MIT"로 바꾸어준다.

"license" 속성은 말그대로 해당 패키지의 라이선스를 지정하는 데 사용되는 속성으로, 라이선스는 패키지의 사용, 배포, 수정 권한을 규정하는 법적 문서이다. 그 안의 ISC( Internet Systems Consortium ) 속성과 MIT( Massachusetts Institute of Technology )은 라이센스 종류 중에 하나로 둘다 모두 간단하고 자유로운 오픈 라이센스로 보편적으로 사용되지만, MIT 라이선스는 저작권 표시를 요구하는 점이 다르기 때문에 MIT 속성으로 변경하여 사용한다.

 

또한 "scripts" 속성의 속성값 객체 내부의 값을 "dev" : "next dev"로 변경해준다. "scripts" 속성은 패키지를 실행하는 명령어를 담고 있는데 위와 같이 속성값을 설정하면 "npm run dev"라는 명령어를 통해서 NextJS를 실행할 수 있게 된다. 우리가 React 프로젝트에서 CRA에서는 "npm start", Vite에서는 "npm run dev"로 명령어를 실행하는 것과 같은 개념이다.

[React, NextJS, React-Dom 설치]

npm install react@latest next@latest react-dom@latest

다음으로 필요한 패키지를 설치해준다. 각각 최신 버전을 설치하는 명령어이다. React의 프레임워크인 NextJS 프로젝트이기 때문에 리액트,넥스트를 설치해주고 react-dom은 React가 생성해준 UI에 대해서 DOM에 렌더해주는 역할을 하기 때문에 같이 설치를 해주어야한다. 

 

NextJS는 우리가 코드를 작성하고 호출을 하는 라이브러리인 React와 달리 우리가 작성한 코드를 호출하는 개념인 프레임워크이기 때문에 NextJS를 실행하면 가장 먼저 app 폴더 내에 있는 page 파일을 찾을 것이다. 프레임워크는 정해진 아키텍처에 맞게 우리가 폴더명과 파일명을 지정해야하기 때문에 반드시 "app" 폴더 내에 작업을 시작해야하고 파일명은 가장 먼저 "page"로 생성해야한다. page 파일의 확장자명은 javascript를 사용한다면 "jsx", typescript를 사용한다면 "tsx"로 생성하면 된다.

[NextJS 실행]

npm run dev

위 명령어로 NextJS를 실행할 수 있게 된다. 이 때 만약 page 파일의 확장자를 "jsx"로 설정했다면 NextJS는 기본적으로 React의 프레임워크이므로 Javascript를 기본적으로 읽을 수 있어서 잘 실행될 것이다.

하지만 확장자를 "tsx"로 설장했다면 터미널에 문구가 하나 생성되며 파일이 실행될 것이다. NextJS는 자체적으로 typescript를 읽을 수 있도록 필요한 패키지를 추가 설치해주며 실행이 될 것이다.

 

⚠ Your page app/page.jsx did not have a root layout. We created app\layout.js for you.

추가적으로 위와 같은 안내 문구가 출력된다.

NextJS는 기본적으로 라우팅을 진행하는 프레임워크이기 때문에 레이아웃이 필수라고 볼 수 있다. 따라서 NextJS는 page파일의 확장자와 상관없이 기본적으로 "layout" 파일을 생성해준다. layout 파일은 필수이기 때문에 우리가 임의로 삭제하더라도 NextJS가 자체적으로 인식해서 파일을 재생성한다. layout에 대해서는 추후에 다시 다루기로 하자.

'NextJS' 카테고리의 다른 글

[NextJS] - (2) Page Router  (2) 2024.09.10
[NextJS] - (1) NextJS  (1) 2024.09.10
[NextJS(v.14)] - (3) Data Fetching  (0) 2024.08.20
[NextJS(v.14)] - (2) Routing  (2) 2024.08.16
NextJS(v.14) - Routing(1)  (4) 2024.07.06

+ Recent posts