next.js13버전이다. app-router 형식이다.
router방식이 프레임 워크에 내장 되어 있어 따로 react-router-dom 과 같은것들의
추가가 필요없다.
src/app.폴더의 layout.jsx가 브라우저의 가장 외곽 라인을 형성하고
app폴더의 page.jsx가 layout 안쪽에 위치하게 된다.
src/app 폴더의 하위폴더의 폴더명은 곧 그안에 있는 page.jsx의 주소값이 된다.
폴더명이 create라면 그 밑의 파일인 page.jsx의 주소는 http://localhost:3000/create 가 된다.
create의 폴더안에 layout.jsx가 존재 한다면 최상단 app/폴더의 layout 밑에 create layout 밑에 page가 존재하게 된다.
넥스트 js의 장점은 서버 사이드 랜더링이기 때문에 seo 에 좋다.
서버사이드랜더링 형식인 서버 컴포넌트들은 빠르게 랜더링되어
ux가 좋다고 한다.
클라이언트사이드랜더링 형식인 클라이언트 컴포넌트들은
클라이언트들과 상호작용이 가능하다.
<layout.jsx> 최상단 src/app 폴더의 layout
import Link from "next/link"; // Link 태그이다. a태그의 상위판으로 a태그와 다르게 SPA를 제공한다.
import "./globals.css"; // 이 파일의 글로벌 css가 적용된다.
import axios from "axios";
import Control from "./Control"; //요건 따로 만든 컴포넌트
// import styled from "styled-components";
export const metadata = {
title: "한지우!",
description: "이몸",
};
export default async function RootLayout({ children }) {
let res = await axios.get("http://localhost:4000/topics", {
cache: {
maxAge: 0, // 캐시 최대 기간을 0으로 설정하여 브라우저 캐시를 사용하지 않음
}, //변경시 따르게 적용되도록 캐시가 안쌓이게 작업했는데 동작을 안함 ㅜㅜ
});
const topics = res.data; //axios로 가져온 값을 담음
return (
<html>
<body>
<h1>
<Link href="/">여기가 가장 상단 레이아웃</Link> // 클릭시 홈으로 이동하게 함
</h1>
<ol>
{/* <li>
<Link href="/read/1">html</Link>
</li>
<li>
<Link href="/read/2"> css</Link>
</li> */}
{topics?.map((item) => {
return (
<li key={item.id}>
{" "}
<Link href={`/read/${item.id}`}>{item.title}</Link> // 맵함수를 돌려서 topic들을 뿌려주고
</li> //클릭시 해당 params를 갖는 페이지로 이동시킴
);
})}
</ol>
{children} //요게 중요한데 //여기 처음 layout 컴포넌트에 파라미터를 보면 props를 받음
<Control /> //그프롭스의 칠드런을 이렇게 세팅하는데
<h3>여기에 푸터 두면 되겄네</h3> //이게 밑에 하위 페이지들이 놓일 자리가 됨
</body>
</html>
);
}
// const Body = styled.body`
// background-color: lightblue;
// `;
<page.jsx> 최상의 page인 홈이다.
import Image from "next/image";
import Link from "next/link";
import ButtonPage from "./Button"; // 버튼페이지는 테스트용으로 만든거라 무시하겠다
export default function Home() {
return (
<>
<h1>여긴 홈이야</h1>
<div>헬로우 넥스트?</div>
<ButtonPage />
<img src="/피카츄.jpg" />엌 ㅋㅋㅋ 피카츄
</>
);
} //별거 없다.
<page.js> read/[id] 폴더의 페이지이다. 폴더명은 곧 주소값이 된다고 했는데 [id]이렇게 해놓으면
주소값에 그 아이디를 갖는다고 한다.
import axios from "axios";
import React from "react";
export default async function Read(props) {
const res = await axios.get(
`http://localhost:4000/topics/${props.params.id}`, // 요기가 신박한다. 일단 이 페이지는 서버컴포넌트라서
{ //훅 사용이 제한된다. 그런데 이폴더는 주소에 아이디를
cache: { //갖는 폴더이다.
maxAge: 0, //그렇기 때문에 props안에 parms안에는 id값이 들어있다 오우
}, //고 아이디에 해당하는 topic하나의 정보를 가져온다.
}
);
const topic = res.data;
return (
<>
<div>{topic.title}</div> //그리고 뿌린다.
<div>{topic.content}</div>
</>
);
}
<page.jsx> create 폴더안에 page이다
"use client"; //생성되는 컴포넌트는 기본적으로 서버컴포넌트인데 요거 써주면
//클라이언트 컴포넌트가 된다.
import axios from "axios";
import { useRouter } from "next/navigation"; // 유즈라우터 라고 useNavigate와 사용법이 비슷하다.
import React from "react"; //넥스트 13버전 이후는 next/router 가 아니라 next/navigation
//으로부터 임포트 받아야한다고 한다. 짜증
export default function Create() {
const router = useRouter();
const onSubmitHandler = async (e) => {
e.preventDefault();
const title = e.target.title.value;
const content = e.target.content.value; //폼태그 하위에 있는 애들의 네임값이 title인놈과 content인놈의벨류
const postTopic = await axios.post("http://localhost:4000/topics", {
title,
content, //타이틀과 컨텐트를 제이슨서버로 투척
});
const lastId = postTopic.data.id;
router.refresh(); //요게 라우터를 랜더링 시킨다고한다.
router.push(`read/${lastId}`); //요게 라우터 이동법인데 뒤에 저 주소값으로 보냄
};
return (
<>
<form onSubmit={onSubmitHandler}>
<div>
<span>타이틀 :</span> <input name="title" />
</div>
<div>
<span> 내용 :</span> <textarea name="content" />
</div>
<div>
<input type="submit" value="생성" />
</div>
</form>
</>
);
}
<page.jsx> update.[id] 폴더의 페이지 이다.
"use client";
import axios from "axios";
import { useParams, useRouter } from "next/navigation"; // 훅이나 온클릭같은 클라이언트 상호작용
import React, { useEffect, useState } from "react"; //하는 것들은 클라이언트 컴포넌트에서만 사용가능
export default function Update() {
const [topic, setTopic] = useState({ title: null, content: null }); //제이슨에서 받아온정보 저장할 유즈스테이트
const router = useRouter();
const params = useParams();
useEffect(() => {
const getInfo = async () => {
const res = await axios.get("http://localhost:4000/topics/" + params.id); //해당 아이디와 일치하는 topic가져오고
const { title, content } = res.data; //거기에 title content 뽑고
setTopic({ title, content }); 스테이트에 저장하고
};
getInfo(); //유즈이펙트로 일회만 함수 실행하고 유즈 이펙트에 [] 빼뜨려서 제이슨 계속 겟하고 난리남 ㅋㅋ
}, []); //조심해야겄음
const onSubmitHandler = async (e) => {
e.preventDefault();
const title = e.target.title.value;
const content = e.target.content.value;
const postTopic = await axios.patch( //수정 로직 patch매서드 써서 유즈 파람스로 가져온 id와
"http://localhost:4000/topics/" + params.id, //일치하는 놈수정
{
title, //요때 타이틀은 인풋값들에서 가져옴
content,
}
);
const lastId = postTopic.data.id; //로직끝나고 해당read페이지로 이동하기 위한 작업
router.refresh();
router.push(`../read/${lastId}`); //요거쓰면 이동
};
return (
<>
<form onSubmit={onSubmitHandler}>
<div>
<span>타이틀 :</span>{" "}
<input
name="title"
value={topic.title}
onChange={(e) => {
setTopic({ ...topic, title: e.target.value }); //
}}
/>
</div>
<div>
<span> 내용 :</span>{" "}
<textarea
name="content"
value={topic.content}
onChange={(e) => {
setTopic({ ...topic, content: e.target.value });
}}
/>
</div>
<div>
<input type="submit" value="생성" />
</div>
</form>
</>
);
}
<Control.jsx> 최상단 레이아웃을 보면 Control 태그가 있다.
"use client";
import axios from "axios";
import Link from "next/link";
import { useParams } from "next/navigation";
import { useRouter } from "next/navigation";
export default function Control() {
const params = useParams(); //layout 페이지는 서버사이드 랜더링의 장점을 위해
const id = params.id; //서버컴포넌트로 놔뒀는데 그안에 이 아이는 유저와의 상호작용이
const router = useRouter(); //필요하여 새로 파일로 분리하고 클라이언트 컴포넌트화했다. 귀찮
//그냥 리액트가 편해 ㅜㅜ
const onDeleteHandeler = async () => {
await axios.delete("http://localhost:4000/topics/" + id);
router.push("/"); //단순로직 유즈파람스로 가져온 아이디에 해당하는 애 지우고 홈으로 이동
};
return (
<>
<ul>
<li>
<Link href="/create">크리에이트</Link>
</li>
{id && ( //아이디가 있는 페이지 에서만 밑에 태그들이 랜더링 됨
<>
<li>
<Link href={`/update/${id}`}>업데이트</Link>
</li>
<input type="button" value="delete" onClick={onDeleteHandeler} /> //그걸 실행하는 버튼
</>
)}
</ul>
</>
);
}
//일단 기본기 연습을 해봤는데 이거 끝나고 리덕스 툴킷을 넥스트에 적용시킬려고 죽쑤고 있고
//다하니까 이젠 다른거에 또 에러나고 next 어렵다..