반응형

리액트에서는 페이지 이동을 위해 react-router와 같은 라이브러리를 추가하여 설정을 해야하지만, 

Next에는 별도로 라우터 설정 파일이 없고 pages디렉토리에 구성에 따라 라우팅 방식이 결정된다.

 

Routes

url : localhost:3000/

source directory: pages/index.js

 

기본 페이지는 전부 index.js로 작성해주면 된다.

"/"

 

url : localhost:3000/about

source directory: pages/about.js

"/about"

 

1depth를 넘어서 2depth이상의 라우팅 설정

url: localhost:3000/movies/all

source directory: pages/movies/all.js

"/movies/all"

 

url: localhost:3000/movies

source directory: pages/movies/index.js

기본 페이지 설정은 역시 index.js가 존재하면 된다.

"/movies"

 

동적 라우팅 생성

 

게시판의 특정 key별로 페이지 이동을 구현하고 싶다면 아래의 형식으로 작성해주면 된다.

> [변수명].js

 

대괄호 안에 동적으로 변경 될 변수명을 넣어주면 된다.

 

url: localhost:3000/movies/1234(변수값)

source directory: pages/movies/[movie_id].js

 

해당 페이지에서 변수 값을 useRoutes를 통해 가져올 수 있다.

query부분에 작성한 변수명 형태로 값이 들어가 있다.

import {useRouter} from "next/router";

export default function Detail(){
    const router = useRouter();
    const {movie_id} = router.query;
    return `detail${movie_id}`;
}

 

결과 페이지

반응형
반응형

NextJS에서는 CSR, SSR을 개발자가 원하는 형태로 작성할 수 있는데, 기존의 리액트방식으로 구성을 하게되면 CSR이고 getServerSideProps를 통해 데이터를 가지고와서 작성하게되면 SSR형태로 구성할 수 있습니다.

이 방식을 채택하게되면 db나 rest api에서 가져온 데이터 리스트들도 서버에서 데이터를 가져와서 props로 넣어주고 시작하기 때문에 static HTML이 작성된 상태를 볼 수 있습니다.

SEO에 최적화된 페이지를 작성할 수 있게 됩니다.

 


getServerSideProps

메소드의 명을 함부로 바꿀 수 없습니다. 주어진 이름 그대로 사용해야 하며 아래의 형태로 기본적으로 사용할 수 있습니다.

export function getServerSideProps(){
    return {
        props: {
            "test": "test"
        }
    }
}

* async 키워드도 옵션으로 사용할 수 있습니다.

 

 

동작 순서

동작 원리는 아래와 같습니다.

  1. 사용자가 페이지에 접근합니다.
  2. getServerSideProps가 존재하는 컴포넌트를 호출해야 한다면, 해당 함수가 먼저 동작이되어서 서버에서 데이터 처리를 합니다.(Rest API, db read 등)
  3. 처리가 끝난 데이터를 return해주는데 props객체 부분에 전달해줄 수 있습니다.
  4. 전달된 props는 _app.tsx에서 작성한 pageProps로 전달받고 이동하려는 컴포넌트에 props를 다시 재전달합니다.
  5. 서버 -> _app.tsx(pageProps) -> components(props) 형태로 전달받은 props를 통해 데이터를 사용합니다.

 

 

사용 예시

export async function getServerSideProps(){
    const {API_KEY} = process.env;
    const {results} = await (await fetch(`http://localhost:3000/api/movies/${API_KEY}`)).json();
    return {
        props: {
            results
        }
    }
}
반응형
반응형

NextJS의 강력한 기능 중 하나인 redirects, rewrites에 대해 정리하고자 합니다.

 

Rest API형태로 데이터를 가져올 때 제공받은 api가 GET형식이고 url자체에 민감한 데이터가 존재한다면 사용자들에게 데이터가 노출되고 타인에 의해 사용량이 초과되거나 문제가 발생할 수 있습니다.

이럴때 next.config.js에 옵션을 설정하여 위의 문제를 방지할 수 있습니다.

 


next.config.js

nextJS의 설정들을 하는 Node모듈 파일입니다.(JSON파일이 아닙니다.)

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true
}

module.exports = nextConfig

nextconfig 객체 내부에 설정할 옵션들을 작성하면 됩니다.

 

 


redirects

redirects를 사용하게되면 들어온 요청 경로(source)의 패턴이 맞을 때, 해당하는 다른 경로(destination)로 리다이렉션을 처리해주는 옵션입니다.

  • source: @string
    해당 서버로 요청하는 url 경로를 입력하는부분으로 패턴형태로도 작성 가능합니다.(request path)
  • destination: @string
    라우팅하려는 경로입니다.(response path) 
  • permanent: @boolean
    • true: 클라이언트와 search엔진에 redirect를 영구적으로 cache하도록 308 status code를 사용
    • false: 일시적으로 cache되지 않는 307 status code를 사용

 

적용 예시

next.config.js

const nextConfig = {
  reactStrictMode: true,
  async redirects(){
    return [
      {
        source: "/old-notice/:fileNo*",
        destination: "/notice/:fileNo*",
        permanent: false
      }
    ]
  }
}

module.exports = nextConfig

설정이 끝나면 서버를 재시작해야 적용됩니다.

 

사용자 url : localhost:3000/old-notice/12345/comments/blahbla-> redirects처리페이지가 리다이렉션되어 변경됨 > localhost:3000/notice/12345/comments/blahbla

 


rewrites

redirects와 비슷하게 생겼고, 사용법도 비슷하다.

차이점이라면 redirects는 request url의 패턴이 일치하면 destination으로 리다이렉션처리를 해주지만, rewrites에서는 request url의 패턴을 확인하고 일치하게되면 페이지 이동이 아닌 매핑주소로 요청을 하게된다.

즉, 요청 주소와 실제로 요청하는 주소가 분리되게 되어 위에서 언급한 민감한 데이터가 포함된 GET방식의 REST API에서 민감한 부분을 제거하여 요청할 수 있게된다.

  • source : @string
    서버로 요청하는 url의 패턴을 작성한다.(hide request path)
  • destination: @string
    실제로 request하는 매핑 주소를 입력한다.(real request path)

사용예시

.env

API_KEY="실제로 동작할 API키"

next.config.js

/** @type {import('next').NextConfig} */
//환경설정에서 작성한 API_KEY를 가져온다.
const API_KEY = process.env.API_KEY;

const nextConfig = {
  reactStrictMode: true,
  async rewrites(){
    return [
      {
        source: "/api/movies",
        destination: `https://api.themoviedb.org/3/movie/popular?api_key=${API_KEY}`
      }
    ]
  }
}

module.exports = nextConfig

"localhost:3000/api/movies" -> "https://api.themovies... ..."

fetch와 같은 비동기함수를 통해 "/api/movies"에 입력을 하게되면 실제로는 destination에 적용된 부분이 매핑되어 원하는 형태의 데이터가 호출된다.

반응형
반응형

리액트 학습이 끝나고 나니 추가로 학습을 해야할 수많은 프레임워크들이 보이기 시작했습니다.

NextJS, Gatsby 등 앱을 개발해야한다면 RN(React Native), Flutter 와 같은 프레임워크들을 학습해야합니다. 이런 프레임워크들은 왜 필요할까요?

 

 

1. Server Side Rendering(SSR)

리액트의 경우 CSR로 이루어져 있습니다. 풀어쓰면 Client Side Rendering(CSR)이라는 뜻인데, 무슨 말일까요?

사용자가 리액트로 작성된 페이지에 접근하면 그떄부터 javascript를 다운로드받고 페이지를 작성하기 시작하면서 HTML구성요소와 CSS, 이미지등 페이지를 작성하는 방식을 말합니다.

이렇게 작성된 페이지의 경우 불안정한 네트워크 환경이거나 javascript를 제한두는 환경에서는 흰페이지의 빈 화면만 사용자는 멀뚱멀뚱 보고 있게 됩니다.

 

아래는 제가 리액트로 작성하고 배포한 페이지를 소스보기한 코드입니다.

소스 정렬을 해봐도 총 22줄밖에 안나오고 div태그 하나밖에 없는걸 볼 수 있습니다.

 

NextJS를 사용하게되면 개발자가 원하는 형태로 CSR, SSR로 구성하여 페이지 동작 방식을 결정할 수 있습니다.

 

1-1. SSR vs CSR

- SSR :

과거에 작성된 대부분의 웹페이지는 SSR방식으로 만들어져 있다.

사용자가 브라우저를 통해 웹 페이지에 접근하면 서버를 통해 요청을하고 요청에 따라 서버는 DB에서 데이터를 가져오거나 통신등 백엔드 처리를 한 뒤 데이터를 노출시키는 형태로 동작되었다. (스프링을 해보셨다면 JSP가 어떻게 노출되는지 생각해보시면 됩니다!) 

대신 초기 로드속도가 빠르고, 검색엔진과 같은곳에 페이지 노출도 쉽다는 장점이 있다.

하지만 페이지를 이동하게 되면 모든 요소를 다시 렌더링하는 비효율적인 방식으로 동작한다.

(헤더, 푸터, 사이드가 공통적으로 작성된 페이지의 경우 해당 요소는 변경될 필요가 없지만 이동때마다 다시 그린다.)

 

- CSR :

모바일이나 패드같은 다양한 인터페이스들이 생기면서 다양한 화면에서 최적화된 페이지를 보여주기 위해 React, Vue와 같은 언어들이 탄생되었고, 이런 언어들은 CSR로 구성되어 동작합니다.

사용자가 페이지에 접근하면 프론트에 대한 리소스만 받아와서 페이지를 렌더링 하기 시작합니다.

데이터와 같은것을 제외한 페이지 관련된 모든것을 받아오기 때문에 초기 로드는 느리다는 단점이 있지만, 한번 페이지가 로드된 후에는 필요한 부분만 변경되는 형태로 구성되어 있기때문에 렌더링에 최적화가 되어있습니다.

 

 

2. 검색엔진 노출(SEO)

NextJS를 사용하게되면 검색엔진 최적화에 적합하여 내가 작성한 사이트를 관련된 키워드로 검색되었을 때, 상위로 노출시키기 좋습니다.

 

아래는 구글의 검색엔진 동작 방식의 흐름도입니다.

구글봇은 페이지를 크롤링 렌더링 대기열에 담아두고 robots.txt를 읽고 크롤링을 허용한 사이트인지 체크를합니다.

허용된 사이트라면 HTML을 파싱하여 구조를 파악하고 ... ...

 

자세한 내용은 아래URL을 참고해주세요. 

자세한 내용 : https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics?hl=ko 

 

자바스크립트 검색엔진 최적화의 기본사항 이해하기 | Google 검색 센터  |  문서  |  Google Develope

Google 검색에서 자바스크립트를 처리하는 방법을 알아보고 자바스크립트 웹 앱을 Google 검색에 최적화하기 위한 권장사항을 살펴보세요.

developers.google.com

 

위에서 언급한대로 리액트의 경우 CSR이며, 페이지에 사용자가 접근하기 전까진 생성되어 있는 노드나 데이터가 없습니다. 검색엔진 측면에서는 빈 페이지를 보기때문에 최적화된 데이터 처리가 어렵습니다.

 

 

3. Static Site Generator(SSG)

Static Site Generator는 정적인 HTML, CSS, Javascript파일을 생성하는 소프트웨어들을 말합니다.

정적인 파일을 생성해서 사용하기 때문에, 웹 서버의 보안에 유리하고, 브라우저에서 다운로드 속도가 빠릅니다.

NextJS로 작성한 페이지는 리액트로 구성하더라도 기본적인 HTML과 같은 기본적인 구조를 가지고 있기 때문에, 아무리 극한으로 느린 네트워크이더라도 페이지의 뼈대(마크업)정도는 보고 있을 수 있습니다.

 

 

# NestJS로 얻을 수 있는 이점

1. 정적인 페이지를 얻을 수 있다.(초기 로드 속도 최적화)

2. SEO에 유리하다.(검색엔진 노출)

3. 페이지 이동시 CSR로 동작하여 SPA장점을 가져갈 수 있다.

 

 

반응형
반응형

반응형으로 잘 처리해두고 깨지지 않는 것도 확인했는데, 특정 IPhone기종에서 몇몇 부분들만 뜬금없이 폰트가 커지게 되어서 UI가 깨지는 형상이 발견되었다.

(chrome, 안드로이드는 모두 괜찮았다...!)

 

body태그에 아래와 같이 한 줄을 추가하여 해결!

body {
    /* iphone 갑자기 폰트 크게 터지는 현상 방지용 */
    -webkit-text-size-adjust: 100%;
  }
반응형
반응형

요즘 리액트 학습을 진행하면서 이것저것 해보고싶은 기술들을 적용해보고, 포트폴리오를 작성하는 시간을 가지고 있습니다.

 

아무래도 아직 문법에 익숙하지 않은 부분도 있고, 소스를 작성하긴 했지만 공통화나 더 깔끔하게 모듈화를 시키고 싶은 욕심도 있는데, 요즘 굉장히 핫한 ChatGpt를 vscode에 연결해서 제가 작성한 코드를 리팩토링하도록 요청을 해보는 시간을 가졌습니다.

 

소스 리팩토링 요청해보기

아래는 제가 공통으로 사용해보려고 임시로 작성해본 firebase에서 데이터를 가져오는 메소드입니다.

export const getStoreToData = (
  CollectionName: string,
  orderName: string
): IDocumentData[] => {
  const array: IDocumentData[] = [];
  const collection = firestore.collection(CollectionName);
  collection
    .orderBy(orderName)
    .get()
    .then((docs) => {
      docs.forEach((doc) => {
        if (doc.exists) {
          array.push({ data: doc.data(), id: doc.id });
        }
      });
    });
  return array;
};

구조가 단순한편으로 작성되어 있어서 CollectionName과 정렬순서만 보장시켜서 데이터를 가져오면 됐는데, 해당 함수를 여러곳에서 동시에 사용하게되면 정상적으로 동작되지 않았습니다.

 

이 소스를 chatGpt에게 요청하니 Promise의 async await구조로 개선하여 알려주었습니다.

// ChatGpt를 활용하여 소스 리팩토링
export const getStoreToData = async (
  CollectionName: string,
  orderName: string
): Promise<IDocumentData[]> => {
  const array: IDocumentData[] = [];
  const collection = firestore.collection(CollectionName);
  const docs = await collection.orderBy(orderName).get();
  docs.forEach((doc) => {
    if (doc.exists) {
      array.push({ data: doc.data(), id: doc.id });
    }
  });
  return array;
};

깔끔하게 정리가 되었고, 매번 섹션별로 제가 firestore를 호출해서 컬렉션별로 하드코딩하여 불러오던 행위를 아래 메소드처럼 변경하여 깔끔하게 공통화를 시킬 수 있었습니다.

 

개선된 메소드 적용해보기

  useEffect(() => {
    // 기존 방식...
    // const array: IWorks[] = [];
    // const collection = firestore.collection("Works");
    // collection
    //   .orderBy("order")
    //   .get()
    //   .then((docs) => {
    //     docs.forEach((doc) => {
    //       if (doc.exists) {
    //         array.push({ data: doc.data(), id: doc.id });
    //       }
    //     });
    //     setWorks(array);
    //   });

    getStoreToData("Works", "order").then((datas) => setWorks(datas));
  }, []);

useEffect에서 매 섹션별로 위 행위를 반복하고 있었는데, chatGpt의 힘을 빌려 개선된 메소드를 적용하고 모든 부분이 깔끔하게 정리되었습니다.

 

코딩을 너무 잘하는 chatGpt를 잘 쓰는법을 연구를 해야할지... 도움을 받아 열심히 또 공부를 해야할지... 굉장히 혼란이 오고 있는 상황입니다.

더더욱 정진해야겠습니다.😪

반응형
반응형

firebase에 개행처리된 데이터를 <br />태그로 치환 처리하여 개행을 처리하고 싶었다.

firebase에 저장된 데이터에는 개행을 처리하고자 하는부분에 "\n" 문자열을 입력하였다.

 

받는부분에선 아래와 같이 처리하여 <br/>태그입력에 성공하였다.

{data.comments.split("\\n").map((p: string, idx: number) => {
    return (
      <span key={`${idx}`}>
        {p}
        <br />
      </span>
    );
})}

key값은 알아서 원하는 값으로 처리하면 될듯하고 split()을 통해 개행으로 구분한 문자열로 나누고 map으로 재조합을 처리해주었다.

 

원하는 형태로 개행처리가 되었다.

반응형
반응형

로컬의 ttf, woff, woff2등 웹 폰트를 적용하는 방법은 아래 글을 참고해주세요.

https://myhappyman.tistory.com/287

 

React - Style component font 적용방법 알아보기(typescript.ver)

리액트에서 styled components를 활용하여 전역 공통 css부분을 적용할 수가 있는데요. 이때 폰트를 적용하는 방법을 알아보고자 합니다. 프로젝트 로컬에 위치한 폰트 연결하기 먼저 프로젝트 내부

myhappyman.tistory.com

 

구글 폰트 적용하기

이번엔 로컬이 아닌 자주 사용하는 구글폰트를 연결하는 방법을 알아보겠습니다.

 

기존의 로컬방식으로 styled-component부분에 import를 하여 간단히 사용하면 될 것으로 생각하였으나, 적용시 아래와 같은 경고메시지와 폰트 적용이 되지 않는 모습을 볼 수 있습니다.

createGlobalStyle인 전역 스타일링 부분에서 import구문을 활용한 적용방식을 추천하지 않으며 react-helmet의 라이브러리를 통해 적용하는 것을 추천한다는 내용입니다.

 

React-Helmet을 통해 폰트 적용하기

react-helmet은 어떤 컴포넌트에서든 사용하게되면 최상단 header에 접근하고 수정하거나 추가하는것을 도와주는 라이브러리 입니다.

 

먼저 설치를 진행해줍니다.

npm install react-helmet

 

 

설치가 완료되면, 원하는 위치에 파일을 생성합니다.

저는 src하위에 바로 생성하였습니다.

 

- src/HelmetComponents.tsx

import React from "react";
import { Helmet } from "react-helmet";

function HelmetComponent() {
  return (
    <Helmet>
      <link rel="preconnect" href="https://fonts.googleapis.com" />
      <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
      <link
        href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
        rel="stylesheet"
      />
    </Helmet>
  );
}

export default HelmetComponent;

이후 컴포넌트 작성하는 방식과 동일하며 Helmet 내부에 원하는 header태그에 해당할만한 내용을 작성해주시면 됩니다.

 

style태그, meta, link, title등 다양한 속성들이 전부 적용이 됩니다.

여기선 구글에서 제공하는 Poppins 폰트 정보를 적용하였습니다.

 

작성이 완료되면 저장을 한 후, index.tsx에서 해당 컴포넌트를 호출하여 적용해주면 됩니다.

 

- src/index.tsx

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import HelmetComponent from "./HelmetComponent";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <React.StrictMode>
  <RecoilRoot>
    <HelmetComponent />
    <App />
  </RecoilRoot>
  </React.StrictMode>
);

 

이것으로 준비는 끝입니다.

 

Global스타일링이나 원하는 위치에서 해당하는 구글 폰트를 font-family를 통해 적용시켜주면 끝입니다.

반응형