React

[Strapi & Next.js] 풀스택 앱 구축하기

얼마나 빠르게 콘텐츠를 추가하는지 보자.

어드민 계정 회원가입 후 바로 로그인된다.
Post라는 컬렉션 타입을 만들어놓고 Title, Slug, Content를 추가해보자.

만든 Post 컬렉션 타입 아래에 new entry를 추가하자.

Save 후에 Publish해서 게시해야 접근이 가능하다.

영상에서는 이렇게 접근하라지만 Strapi V4는 url에 /api가 먼저 있어야 한다.
즉, /api/posts 로 요청을 보낸다.

영상

실습

이렇게 들어오면 잘 구현된 것이다. json 형식이 많이 달라졌다.

권한 이슈를 해결할 것이다. public에서는 find, findone만 지원하도록 하겠다.

아래와 같이 이동 후 find, findone을 체크한 뒤 Save한다.

영상에서는 APPLICATION을 수정한다.

설명을 보면 Define all allowed actions for the application plugin.이라고 한다.

실습할 때 보면 Post를 수정하면 된다. 위에서는 count를 가지고 있다.

이에 대해서는 아래에서 다룬다.

이제 다시 요청보내면 컨텐츠 데이터가 잘 들어온다.

기본적으로 CRUD에 대한 스캐폴딩을 지원하는 앱답게 페이지네이션 정보까지 meta에 담아서 넘겨준다. V3 버전에 비해 발전한 것으로 보인다.

영상

실습

count API를 사용해보자고 찾아보니 meta 내에 total로 들어갔음을 알려주고 있다.

포스트 작성자를 정의하는 속도가 얼마나 빠른지 확인해보자

Content-Type Builder로 이동한다.
Add another field 를 클릭한다.
Relation을 선택한다.

작성자가 유저일 것이고 users-permissions의 authenticated user일 것이다.

우측 박스의 Post 옆 드롭다운 버튼을 누른다.

사용자는 많은 포스트를 가지고 있을 것이다.

명확성을 위해 user를 User로 대문자로 바꾸라고 한다. 하지만 V4이므로 그대로 둔다.(그대로 사용하는 케이스를 확인함)

완료되었으니 Save하자.

이제 기존 포스트는 작동하지 않는다. 유저를 추가해주고 포스트를 할당할 것이므로 Users 밑에 new entry를 추가한다.

영상

실습

기본적으로 이메일 확인을 진행하므로 TRUE를 체크한다.
Relations에서 posts를 추가한뒤 Save하자.

Post 확인

영상에서 node.js로 할 수 있는 모든 걸 strapi로 할 수 있냐고 묻는데, api/controllers로 이동해서 post.settings.json을 보면 지금까지 변경한 사항을 볼 수 있다고 한다. V4에는 이 위치에 이런 파일이 없다.

그럼 어디에 있는가? 위치와 이름이 좀 다르다. controllers 하위가 아니고 content-types/posts/schema.json 에 있다.

포스트들을 더 추가한다.

유저 추가해서 포스트들을 할당해주자.

sean
brino
kevin

요청해서 데이터 확인해보자

영상

실습

User정보가 들어오지 않고 있다. 이걸 해결했다.

Next.js 생성

npx create-next-app blog-next cd blog-next
TypeScript
복사
Next.js의 index.js를 수정해본다.
import Head from "next/head"; import Image from "next/image"; import styles from "../styles/Home.module.css"; export default function Home() { console.log("i am on the client side"); return <div className={styles.container}>Hi~</div>; } export async function getStaticProps() { console.log("i am on the server side"); return { props: {}, }; }
TypeScript
복사
Next.js는 두가지를 할 수 있다. 노드를 사용해서 서버에서 데이터를 생성하거나, 클라이언트에서 데이터를 생성하는 것이다. 둘다 할 수 있는 풀스택 앱이다.
프리렌더를 위해서 getStaticProps(Static Generation)내에서 로직을 수행하면 된다.
페이지를 로드할 때를 확인해보면 터미널에서 서버에서 실행된 로직을 확인해볼 수 있는데, getStaticProps 함수와 컴포넌트 모두 실행됨을 확인하게 된다.
즉, 프리렌더된 것이다.
또한, 클라이언트 측에서 실행되는 콘솔 출력(i am on the client side)도 확인할 수 있다.

만들어놓은 strapi 서버의 엔드포인트에서 데이터를 받아온다.

영상

실습

데이터 구조가 GraphQL스럽게 변해서 그런지 코드가 많이 달라졌다. 어쨌든 똑같은 출력을 볼 수 있다.
import Head from "next/head"; import Image from "next/image"; import styles from "../styles/Home.module.css"; export default function Home({ posts }) { console.log(posts); return ( <div> {/* loop ever the posts and show them */} {posts.data && posts.data.map((post) => ( <div key={post.id}> <h2>{post.attributes.Title}</h2> </div> ))} </div> ); } export async function getStaticProps() { // get posts from our api const res = await fetch("http://localhost:1337/api/posts?populate=*"); const posts = await res.json(); console.log(posts); return { props: { posts, }, }; }
TypeScript
복사

포스트를 더 추가해보자(유저도)

5개가 된 포스트를 확인한다.

강사는 strapi를 어디서 호스팅하냐는 질문에 digitalOcean을 언급했다.

만약 첫문자를 로워케이스로 바꾸고 싶다면 해당 entry를 클릭해서 수정하면 된다. 뭐가 더 나을지는 아직 확실치 않다.

원하는 사람은 바꾸면 된다.

post별 username을 보여주자.

깊게도 들어 있다. 이걸 다 걸러내서 가져올 수 있기 때문에 그래프큐엘이 각광받는 것일까?
한국에서는 트렌드가 늦고 잘 사용하는 사람이 적다고 하는데, RESTful API의 대체제인 그래프큐엘을 메인으로 내세우려고 하는 게 아닐까 싶다.
물론 그래프큐엘은 단일 API이므로 데이터 형식이 유연해서 캐싱 구현하기에 더 복잡해지는 등의 단점은 있다고 한다.
... export default function Home({ posts }) { console.log(posts); return ( <div> {/* loop ever the posts and show them */} {posts.data && posts.data.map((post) => ( <div key={post.id}> <h2>{post.attributes.Title}</h2> <p> {post.attributes.users_permissions_user.data.attributes.username} </p> </div> ))} </div> ); } export async function getStaticProps() { ... }
TypeScript
복사

[Slug] 페이지를 만든다. 각 포스트별 상세페이지를 만드려고 한다.

slug라는 파라미터에 따라 적절한 값이 들어오는지 확인해본다. 영상에서는 하나만 들어오는데, 실습하면 다 들어오고 있다.
쿼리 파라미터가 다 무시되고 있는듯 하다.

StaticProps에서 url 파라미터로 들어온 slug를 가지고 findone 요청하려고 했으나 data는 잘 찍히지만 모든 data가 다 들어오고, post가 undefined가 찍힌다. v3이랑 형식이 많이 달라져서 그렇다.

먼저 Slug=${slug}라는 쿼리가 안통하는 것 같다.

놀랍게도 Docs를 보면 쿼리 방법이 나와있다. 이렇게 하면 된다.

fitler[field]=value 이런 식으로 하면 된다.
data는 잘 들어왔으니 post로 가공해보자.
... const post = data.data[0]; ...
TypeScript
복사
export default function Post({ post }) { return ( <div className="post"> <h1>{post.attributes.Title}</h1> </div> ); } export async function getStaticPaths() { const res = await fetch(`http://localhost:1337/api/posts`); const posts = await res.json(); // paths will be an array of the paths to the posts const paths = posts.data.map((post) => ({ params: { slug: post.attributes.Slug, }, })); return { paths, fallback: false, }; } export async function getStaticProps({ params }) { const { slug } = params; const res = await fetch( `http://localhost:1337/api/posts?filters[Slug]=${slug}` ); const data = await res.json(); const post = data.data[0]; return { props: { post, }, }; }
TypeScript
복사

Next.js는 html 파일을 정적으로 제공하므로 매우 빠르다.

보통 gatsby를 사용해서 배포하곤 한다.
export 스크립트를 추가한다.

실행해서 minify된 파일들을 확인해보자. 페이지별로 파일이 있다.

강의에서 보여주는 거랑 달리 .next 폴더 내에 있다. out 폴더는 js파일들이 들어있다.

여기서 Next.js SSG에 대한 중요한 팁이 나온다. Static generation을 쓰게 되면 하나의 기사가 변경된 경우 모든 파일을 다시 빌드해야 되는 과정이 필요합니다. 따라서 전자상거래 사이트에 제품이 1만개 이상 있는 것 같은 예시를 본다면 Incremental static generation(정적 증분 생성)을 사용하면 좋을 것이다.

영상

그러면 얘기하려 했던 fallback 을 true로 변경한다.
404페이지로 그냥 보여주기 전에 해당 페이지를 찾아보도록 한다.
꽤 큰 동적 앱을 구축하기 위한 훌륭한 전술이다.

다음으로 Comment를 추가한다.

Content-Type Builder로 가서 create a collection type을 누르고 comment 생성

강사는 가지고 있던 블로그 스트랩을 가지고 digitalOcean에서 얼마나 편리하게 배포를 지원하는지 보여준다.(디지털 오션 유튜브 채널임)

영상

백엔드 blog-strapi를 배포한 다음 어드민 패널에서 create 등의 권한을 허용해준다.

insomnia에서 요청을 보내서 동작을 확인한다.

댓글을 가지고 있지 않음을 확인했다.

실습 (vscode extension의 Thunder client 사용)

create 허용

에러 발생

형식이 v3와 달라졌기 때문에 data라는 프로퍼티에 객체를 담아 줘야 한다.

생성 완료