Next.js - 0 : 소개
넥스터js SSR 지원
js 가 동작하지 않는 환경에선 화면이 표시되지 않기 때문에 검색엔진과 같은 로봇들이 컨텐츠를 이용 못함
자바스크립트를 다운로드 하고 실행하기전에는 화면이 안그려짐
ssr : 서버 쪽에서 자바스크립트 실행 -> 브라우저로 완성된 html이 전송됨
자바스크립트 실행이 안되도 화면이 보임(검색엔진 친화적)
다운로드 받는 즉시 실행되기 때문에 페이지가 바로 표시되어 ux적으로도 훌륭
next.js 13 version
app router라는게 나옴. (13버전 이전에는 page router를 사용함)
Server component라는것도 나옴.
Next.js - 1 : 설치 및 실행
터미널에 npx create-next-app@latest 입력해서 다운로드
npm run dev로 실행 (npm run start는 create-react-app에서 명령)
터미널에 뜬 http:// ~~3000번 열면 시작됨
(tailwindcss랑 nextjs랑 관련있음?)
Next.js - 2 : 샘플앱 세탁
src/app 밑의 layout.js이 기본적인 웹페이지의 골격을 구성함
html의 en삭제
body의 classname 삭제
Inter 관련 모두 삭제
children 자리에는 page.js에서 return한게 들어옴.
page.js의 main 다 지우고
globals.css 내용 다 지우고 저장하면 끝
Next.js - 3 : 배포
개발이 끝났다 치고 배포하는 법부터 알기
열린 페이지의 개발자도구에서 network 탭의 맨 밑 6.3MB resources 부분뜻
: 서버에서 클라이언트로 날아오는 데이터
너무 큼. 해결책은?
npm run build로 서비스 되기 위한 파일들로만들어줌
npm run start해주면? 294kb로 축소됨.
Next.js - 4 : 뼈대 만들기
공통된 걸 다룰 때는 layout.js에서 다룬다.
page에서 만든 부분은 children 부분으로 들어온다.
create update delete 기능도 제공할 것이기 때문에
Next.js - 5 : 라우팅
app 밑에 create라는 폴더 만들고 하위에 page.js라는 약속된 파일을 만듬
동작과정:
create로 들어가면 app폴더 밑에 create폴더를 찾음. (그 폴더에서 page.js를 찾음)
그 page.js의 내용을 어디에 결합하느냐? layout.js를 거슬러 찾아올라감.
create 폴더가 layout.js 파일을 갖고 있다면 그것과 결합함. 없으면 그 부모로 올라가서 또 찾음.
그 안의 children에 결합.
create폴더 안에 page.js와 layout.js가 모두있고 page.js를 그 폴더의 layout.js에 넣고 싶으면 layout 컴포넌트의 매개변수로 props넣어주고 {props.children}으로 사용
export default function Layout(props){
return (
<form>
<h2>Create</h2>
{props.children}
</form>
)
}
다이나믹 라우팅
read/1
read/2
app 밑에 read폴더 밑에 [id] 밑에 page.js 만들어줌
[id]는 다이나믹 라우팅, 즉 여러가지 숫자가 올 수 있음
read밑의 폴더명 [id]를 아래와 같이 page.js에서 연결 시킬 수 있음.
{props.params.id}
Next.js - 6 : Single Page Application
웹페이지의 개발자도구 메뉴에서 run command > disable javascript를 했을 때
react로 만든거면 화면이 안나오지만,
next.js로 만드거면 나온다
why?
사용자가 요청하면 서버쪽에서 리액트에서 실행해서 그 응답결과를 .next에 넣어주고 그걸 응답해주기 때문
즉, next.js는 자바스크립트가 아닌 html을 응답
확인했으면 다시 javascript활성화
next의 단점
개발자도구 network에서 속도를 줄여보고 reload해보면 속도가 느리다.
다른 페이지로 넘어가도 속도가 느리다
이미 방문한 곳이더라도 또 다시 다운받고 보여준다
이걸 극복하기 위해선? a태그를 Link컴포넌트 태그로 바꾸면 됨
웹페이지가 여러페이지임에도 불구하고 한페이지처럼 작동하는게
SPA(Single Page Application)
Next.js에서는 a태그를 Link로만 바꿔도 SPA가 됨
Next.js 13 - 7. 정적 자원 사용하기
public 폴더에 갖다 놓고 원하는 파일에서 아래처럼 쓰면됨
<img src="/hello.png"></img>
Next.js 13 - 8. css
루트 layout.js에 있는 어떤 페이지를 방문하건간에 global.css가 작동
Next.js 13 - 9. backend
하드코딩돼있는걸 백엔들르 구축해서 동적으로 가져와서 표시해줄 거임
(메뉴얼의 Routing> Route Handlers를 확인하면 next.js로 Api구축도 배울수있음)
json서버를 이용해서 서버구축!
npx json-server --port 9999 --watch db.json
명령어를 치면 db.json 파일이 하나 생김
뜻 : 이 서버를 9999포트에서 실행시키고, db.json에 정보를 저장하고, 정보가 바뀌면 바로 서버에 반영한다(--watch)
아래와 같이 새롭게 추가할 수도있음
"topics": [
{
"id": 1,
"title": "html",
"body": "html is..."
},
{
"id": 2,
"title": "css",
"body": "css is..."
}
],
저 데이터를 우리 자바스크립트로 가져오는 행위
서버랑 통신할 때 사용하는 패치명령어 사용 >>
fetch().then().then();
fetch('http://localhost:9999/topics')
.then((resp)=>{
return resp.json(); //서버가 우리한테 준건 json이야 그니까 우리가 읽을수 있는 js로 컨버팅해 라는 뜻
})
.then(result=> {
console.log('result', result);
});
Next.js 13 - 10. 글목록 가져오기
리액트 18버전부터 서버컴포넌트 개념 추가됨
그걸 next.js가 가지고옴
서버컴포넌트에서 useState, useEffect, onClick, onChange와 같은 api를 사용하면 에러발생함
서버컴포넌트와 클라이언트 컴포넌트는 서로 사용할 수 있는 api가 다르다
next.js에선 특별한 조치를 취하지 않으면 그건 서버 컴포넌트로 간주함
사용자와 상호작용 X(정보 단순히 보여주기만하는거) -->> Server component로 만드는게 유리
사용자와 상호작용 O -->> Client component로 만드는게 유리
서버가 상파울루에 있고 우리가 서울에 있으면
fetch하는데 시간이 오래 걸린다
개발자도구 run command 에서 disable javascript를 하고 리로드하면 useEffect로 동적으로 데이터 가져오는 부분은 못가져옴.
서버컴포넌트로 만들기 위한 방법
aysnc를 function에 붙여준다.
내부적으로 await라는 promise를 사용하기 위해
const [topics, setTopics] = useState([]);
useEffect(() => {
fetch("http://localhost:9999/topics")
.then((resp) => resp.json())
.then((result) => {
setTopics(result);
});
}, []);
비동기적으로 코드를 작성하지않고 아래처럼 작성
const resp = await fetch("http://localhost:9999/topics"); // 1. 얘가 실행이 끝날때까지 await const topics = await resp.json(); // 2. json으로 바꾸라는 명령이 전달 {topics.map((topic) => { return ( <li key={topic.id}> <Link href={`/read/${topic.id}`}>{topic.title}</Link> // 3. 동적으로 생성 </li> ); })}
장점
1. 용량이 적다.(클라로 js를 전송하지 않으니까)
2. 접근하고자 하는 주소가 같은 서버라면
fetch를 사용하고 있는 layout.js와 fetch에서 사용하고 있는 주소의 api서버가 같은 서버라면 빠르게 동작이 완료됨
3. 서버쪽에서 렌더링을 끝내고 보내기 때문에 run command에서 disable javascript를 해도 동작됨
왜? 서버쪽에서 동적으로 생성한 정적인 내용을 클라이언트로 전달했으니까
Next.js 13 - 11. 글 읽기
// 생각해야한다. 해당 페이지가 사용자와 상호작용 하는가?
// 상호작용 없이 데이터를 읽어서 출력할 뿐 >> 이런건 서버 컴퍼넌트로 가면됨
export default async function Read(props) {
const resp = await fetch(`http://localhost:9999/topics/${props.params.id}`);
const topic = await resp.json();
return (
<>
<h2>{topic.title}</h2>
{topic.body}
{/* parameters : {props.params.id} */}
</>
);
}
Next.js 13 - 12. 글생성
파일경로 에러가 생기면 .next 폴더를 지우기 위해
rm -rf .next 후 ( powershell 에선 실행 안되니 터미널에서 git bash를 연다 ) (터미널 +버튼 오른쪽 아래방향 화살표 누르면 나옴)
npm run dev를 하면 .next 폴더를 다시 만든다.
fetch가 안되는 에러발생
이유를 보니 > npx json-server --port 9999 --watch db.json 이 명령어가 꺼져있었음. 서버가 죽어있는 상태였으니까 서버에다 "뭐좀 줘" 해도 반응이 없었던거임
import { useRouter } from "next/navigation"; // next/router는 next 12버전
const router = useRouter(); // 클라이언트 컴포넌트에서만 사용
router.push
fetch(`http://localhost:9999/topics`, options) // 2번쨰로 인자로 인해 서버쪽으로 데이터가 전송됨
.then((res) => res.json())
.then((result) => {
console.log(result);
const lastId = result.id;
// router로 방금 생성한 글로 리디렉션 할 수 있음
router.push(`/read/${lastId}`);
});
Next.js 13 - 13 cache
create 버튼을 누르면 글목록이 갱신 되어야 하는데 이건 cache 와 관련있음
GET http://localhost:9999/topics/3 200 in 1ms (cache: HIT)
*HIT 는 cache를 사용했다는 뜻
GET http://localhost:9999/topics/3 200 in 1ms (cache: MISS)
*MISS 새로 만들었다. 캐시를 사용하지 않았다
한번도 access 한적이 없거나
데이터가 더이상 유용하지 않아서 다시 가져온다
reload를 하면 json서버에서 통신이 이뤄지지 않는다.
왜? 캐시를 사용했으니까( cache : HIT)
cache 관련 공부
공식문서 > data fetching > caching
router.refresh(); // 서버컴포넌트를 refresh 서버컴포넌트가 새로 데이터를 가지고 와서 그 결과를 뿌려준다
서버 컴포넌트 refresh하기 전에 layout.js에서 fetch 인해 만들어진 캐시를 없앤다.
방법은?
1. fetch의 두번째인자로 { next : { revalidate : 10 }} >> '10초동안만' 캐시를 유지하겠다
근데 이처럼 하면 글을 생성하고 페이지를 리다이렉션했을 때 바로 캐시가 없데이트 되지 않기 때문에 0으로 함( 그러면 캐시를 쓰지 않겠다는 뜻)
또는
2. fetch의 두번째인자로 { cache : 'no-store' }
// 성능은 희생될 수 있으나 잠시 단순함을 위하여.. (아직 어디에 더 쓰이는지는 봐야함)
위와 같이 하면 새로고침했을 때 json 서버와 새롭게 통신을 한다.
Next.js 13 - 14. update & delete버튼구현
사전작업 : 메인 페이지에서는 update, delete 를 없애자
특정 글 목록에 선택해서 들어갔을 때 생기게 하자
root layout.js에서는 기본적으로 아이디 값을 확인할 수가 없다.useParams(Client Component Hook)
루트 레이아웃 모듈안의 특정 함수만 클라이언트로 바꿀 순 없으므로 새로운 파일로 만들어줘야함.파일내의 필요한 부분만 클라이언트화 시키면됨.
"use client";
import Link from "next/link";
import { useParams } from "next/navigation";
export function Control() {
const params = useParams();
console.log("params>>", params);
const id = params.id;
return (
<ul>
<li>
<Link href="create">Create</Link>
</li>
{id ? (
<>
<li>
<Link href={"/update/" + id}>Update</Link> // 이렇게 해주면 url 링크가 원하는 방식으로 변경
</li>
<li>
<input type="button" value="delete" />
</li>
</>
) : null}
</ul>
);
}
http://localhost:3000/update/4 를 구현하려면..Next.js 13 - 15. 글수정
useEffect(() => {
fetch(`http://localhost:9999/topics/` + id)
.then((res) => res.json())
.then((result) => {
setBody(result.body);
setTitle(result.title);
});
}, []);
읽어온 값을 title, body 폼에 넣기 위해선 state를 따로 만들어줘야함 onChange(e=> setTitle(e.target.value));
이유는 ? >상세페이지 캐시를 꺼주지 않았기 때문(디폴트는 캐시상태임을 알 수 있음)
수정할 땐 method : "PUSH" or "PATCH"
Next.js 13 - 16. 글 삭제
onClick={() => {
const options = { method: "DELETE" };
fetch(`http://localhost:9999/topics/` + id, options) // delete일떄도 이 fetch가 왜필요하지? >> 가져와서 지워야 하니까
.then((res) => res.json())
.then((result) => {});
}}
삭제를 했으면 메인페이지로 리다이렉션 시키기 >> router 이용
import { useRouter } from "next/router"; 자동 생성 되는데
next/router에서 router는 12버전이고 navigation으로 바꿔줘야함
router.refresh();
router.push("/");
Next.js 13 - 17. 환경변수 & 졸업
개발할 때 사용하는 api와 디플로이해서 실서버에서 사용하는 api주소가 다를 수 있다.
fetch를 할 때 주소가 하드코딩 돼있는것을 따로 분리해서 관리해줄 필요가 있음
이때 봐야하는거 > 환경변수
홈디렉토리에 .env.local에서 다룬다
루트 레이아웃에서 process.env.API_URL로 가져온다.
*환경변수에는 pw 와같이 기밀이 중요한 데이터가 포함될 가능성이 높다
이런것들까지 클라이언트에 노출이 된다면 저 정보가 브라우저로 전송이 될 것이고 이건 유출과 직결된다.
고로, 이런 문제를 막기위해서 환경변수는 기본적으로 서버컴포넌트인 레이아웃.js에서만 사용 가능하게 해뒀다.
fetch(process.env.API_URL + "topics", {
cache: "no-store",
});
create(클라 컴포넌트)에서도 한번 해보자
fetch부분을 process.env.API_URL로 바꿨더니 undefined가 뜬다
이유는>> 기밀정보가 들어있는 env.파일은 기본적으로 서버컴포넌트에서만 사용가능하다
웹브라우저를 위한 환경변수를 쓰고 싶다면?
.env.local에 만들어둔걸 NEXT_PUBLIC을 접두사로 붙이면됨
예) NEXT_PUBLIC_API_URL
이모든게 보안때문에 존재함
그럼 버전관리할 때 환경변수를 어떻게 관리?
.gitignore파일에서 next.js개발 환경 셋팅할때부터 있었음
'이러이러한 파일은 버전관리하지마세요' 라는 권고하고 있는 부분이 있음
아래부분임 ㅇㅇ
# local env files
.env*.local
애초에 버전관리를 위와같이 막아둠
이러해서 조심할 필요가 없음.
.env.local의 형식을 알아야 그걸 보고 env를 만들거잖음?
이땐 .env.local똑같은 파일 복사
그리고 .env.local.example에 샘플정보를 담아둔다
나중에 다른 개발자가 이 개발환경을 다운받아 설치할 때 확장자가 env인 파일을 보고 env로 바꾼다음에 자기한테 맞는 정보를 추가한걸 통해서 개발환경을 구축할 수 있다
위와같이 해야 커밋하고 다른 개발자가 볼 수있자나. 원본은 ignore 돼있으니까
끝
많은 것을 배웠습니다, 감사합니다.