라우터(router)
객체란, 페이지 이동 및 주소와 관련된 기능을 가지고 있는 객체이다.
router를 사용해서 A 페이지에서 B 페이지로 이동할 때, "B 페이지로 라우팅한다"
고 말한다.
next.js에서 useRouter를 제공해준다.
import Router from 'next/router'
export default function Routing() {
const handleClickPathname = () => {
const pathname = Router.pathname
alert(pathname)
}
const handleClickAsPath = () => {
const asPath = Router.asPath
alert(asPath)
}
const handleClickBack = () => {
Router.back()
}
const handleClickPush = () => {
Router.push('/이동할 페이지') // https는 제외한다.
}
const handleClickReload = () => {
Router.reload()
}
const handleClickReplace = () => {
Router.replace('/')
}
return (
<>
<button onClick={handleClickPathname}>현재 위치 주소: Router.pathname</button><br/>
<button onClick={handleClickAsPath}>현재 위치 주소: Router.asPath</button><br/>
<button onClick={handleClickBack}>뒤로가기 버튼: Router.back()</button><br/>
<button onClick={handleClickPush}>현재 페이지에서, 다른 페이지로 이동: Router.push()</button><br/>
<button onClick={handleClickReload}>새로고침: Router.reload()</button><br/>
<button onClick={handleClickReplace}>현재 페이지 삭제 후, 다른 페이지로 이동: Router.replace()</button><br/>
</>
)
}
/boards 페이지는 누가 언제 접속해도 항상 게시판 등록 페이지가 나타난다.
이러한 페이지로 이동하는 것을 "정적 라우팅한다"
고 한다.
반면 게시판 상세보기와 같은 경우, 글 번호에 따라서 주소가 변경된다.
하지만, 이러한 경우에는 게시글이 100개, 1000개가 넘어가게 되면 각각의 글 번호에 따라 페이지를 100개, 1000개씩 만들어 정적라우팅을 해주기는 어렵기 때문에 이러한 라우팅을 효과적으로 처리하기 위해서 동적 라우팅을 사용한다.
/board/1 ⇒ 1번 게시글 상세보기 페이지
/board/2 ⇒ 2번 게시글 상세보기 페이지
/board/3 ⇒ 3번 게시글 상세보기 페이지
이러한 페이지로 이동하는 것을 "동적 라우팅한다"
고 한다.
next.js에서는 동적 라우팅을 제공해주는데,
위의 폴더 구조와 같이 보여주고자 하는 폴더 이름의 하위 폴더로 [boardId]
폴더를 만들어 준 후,
이 안에 index.js
파일을 만들어주면 동적 라우팅을 사용할 수 있다.
대괄호로 감싸준 폴더를 만들어주면 이동해주고자 하는 페이지 번호, 혹은 게시글 번호가 대괄호 안에 쓰여진 변수명에 담겨져 그 변수 안에 있는 데이터를 꺼내 조회할 수 있다.
실제로 router.query = { boardId: 1 }
이런 형식으로 들어가게 되면서 자동으로 게시글 번호를 꺼내올 수 있다.
📌 이때, 주소는 무조건 문자이다
-> 문자를 숫자로 변환해준다.
게시물 조회시 useQuery
를 사용한다.
const {data} = useQuery() // {} 안에 정해진 변수 data를 넣어준다.
import { gql, useQuery } from "@apollo/client";
import { useRouter } from "next/router";
const FETCH_BOARD = gql`
query fetchBoard($number: Int) {
fetchBoard(number: $number) {
number
writer
title
contents
}
}
`;
export default function StaticRoutedPage() {
const router = useRouter();
const { data } = useQuery(FETCH_BOARD, {
variables: { number: Number(router.query.number) }, // 데이터 타입을 숫자로 변환해준다.
}); // 비동기 방식
console.log("============");
console.log(data); // undefined (비동기 방식이기 때문)
console.log("============");
return (
<>
<div>{router.query.number}번 게시글로 이동이 완료되었습니다</div>
<div>작성자: {data?.fetchBoard.writer}</div> // 옵셔널 체이닝
<div>제목: {data?.fetchBoard.title}</div>
<div>내용: {data?.fetchBoard.contents}</div>
</>
);
}
javascript는 작성된 코드가 상단에서부터 순서대로 실행되기 때문에,
데이터를 요청하고 응답을 받아오는 동안 화면에 그려질 데이터의 내용이 undefined이므로
첫 화면이 그려지는 시기에 데이터를 불러오면서 에러가 발생한다.
이 부분이 효율적으로 실행되기 위해서 화면을 미리 그려놓고 데이터를 그려주기 위해서 조건부렌더링을 사용한다.
조건부 렌더링에는 &&연산자, 삼항연산자, 옵셔널체이닝이 있다.
data는 동기적으로 받아와야하는 데이터이다.
그러나 데이터가 오기 전에 이미 return 부분에서 rendering을 해주고 있기 때문에,
삼항 연산자를 사용하여 데이터가 있을 때, 없을 때를 모두 적어준다.
data ? data.fetchProfile : undefined
data && data.fetchProfile
&&연산자는 데이터가 없을 경우 자동으로 undefined를 반환한다.
데이터가 없을 때 따로 div를 쓸 필요가 없으면 else 부분을 쓸 필요가 없다.
&&연산자는 앞의 값이 참일 경우에만 뒤의 값을 보여주었는데, 반대로 앞의 값이 거짓일때 뒤의 값을 보여주는 경우도 있다.
Nullish coalescing 연산자라 불린다.
??연산자는 앞의 값이 빈 값이면 뒤의 값을 보여주며 ||연산자는 앞의 값이 거짓(false)일 경우 뒤의 값을 보여준다.
data ?? data.fetchProfile
data || data.fetchProfile
optional-chaing
이란, 기존의 && 연산자를 쓰면서 길어졌던 코드를 더욱 간결하게 사용하는 연산자이다.
optional-chaing은 ES2020에서 나온 최신 문법이다.
data?.fetchProfile
optional-Chaining은 ? 연산자 앞 객체의 참조가 undefined || null 이라면 undefined를 리턴해준다.
위에 있는 삼항연산자, && 연산자와 똑같은 기능을하지만, 훨씬 간단해졌다.
data ? data.fetchProfile : undefined // 삼항 연산자
data && data.fetchProfile // && 연산자
data?.fetchProfile // 옵셔널 체이닝
graphql로 mutation시 항상 성공하는 것은 아니다.
Backend 컴퓨터에 문제가 발생할 수도 있고, 내가 수정하려는 게시물이 갑자기 삭제가 되는 바람에 수정에 문제가 발생하는 등 여러가지 실패 가능성이 있다.
따라서, API 요청시 성공에 대한 처리, 실패에 대한 처리를 나누어 작업해야 한다.
try {
await createBoard({
variables: {
aaa: "훈이",
bbb: "1234",
ccc: "안녕하세요 훈이에요",
ddd: "반갑습니다"
}
})
} catch(error) {
alert(error.message) // 백엔드 개발자가 보내주는 실패 메시지
} finally {
// 성공, 실패 여부와 상관없이 무조건 마지막에 실행되는 부분
// 필요없다면 생략 가능
}
📌 shorthand property
shorthand property는 객체를 정의할 때 객체의 key값과 value 값이 같으면, 각각 표기하지 않고 한 번만 표기하는 것을 의미한다.const result = await createUseditem({ variables: { createUseditemInput: { name // name: name remarks // remarks: remarks contents. // contents: contents }, }, }, });