라우터(router) 객체란 페이지 이동과 관련된 가능을 가지고 있는 객체입니다.
이 객체를 사용해서 A페이지에서 B페이지로 이동할 때, "B 페이지로 라우팅한다"라고 합니다.
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('/')
}
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/>
</>
)
}
더 많은 내용은 Next.js Router를 참고해 주세요.
/login 페이지는 누가 언제 접속해도 항상 로그인 페이지가 나옵니다.
이러한 페이지로 이동하는 것을 "정적 라우팅한다"라고 합니다.
게시판 상세보기와 같은 경우, 글 번호에 따라서 주소가 변경됩니다.
하지만 이러한 경우에는 게시글이 100개, 1000개가 넘어가게 되면 각각의 글 번호에 따라 페이지를 100개, 1000개씩 만들어 정적라우팅을 해주기는 어렵기 때문에 라우팅를 효과적으로 처리하기 위해서 동적 라우팅을 사용합니다.
/board/1 👉 1번 게시글 상세보기 페이지
/board/2 👉 2번 게시글 상세보기 페이지
/board/3 👉 3번 게시글 상세보기 페이지
/board/4 👉 4번 게시글 상세보기 페이지
...
이러한 페이지로 이동하는 것을 동적 라우팅한다라고 합니다.
Next.js에서는 동적 라우팅을 제공해줍니다.
폴더 구조는 pages 👉 boards 👉 [boardId] 👉 index.jsx(혹은 tsx) 이렇게 됩니다.
대괄호로 감싸준 폴더를 만들어주면 이동해주고자 하는 페이지 번호, 혹은 게시글 번호가 대괄호 안에 쓰여진 변수명에 담겨져 그 변수 안에 있는 데이터를 꺼내 조회할 수 있습니다.
이때, 대괄호 안에 쓰여지는 폴더 이름은 단순히 변수명이기 때문에 아무렇게 작성해도 상관없습니다.
실제로 router.query = { boardId: 1} 이런 형식으로 들어가게 되면서 자동으로 게시글 번호를 꺼내올 수 있습니다.
아래의 코드는 GraphQL을 통해 동적으로 게시글을 불러오는 예제입니다.
import { gql, useQuery } from "@apollo/client"
import { useRouter } from "next/router"
// 여기서 'number'를 받고 query를 통해 fetchBoard 메소드를 실행시켜준다
const FETCH_BOARD = gql`
query ($number: Int) {
fetchBoard(number: $number) {
number,
writer,
title,
contents,
}
}
`
const staticRoutedPage = () => {
const router = useRouter()
const {data} = useQuery(FETCH_BOARD, {
variables: {
// router.query.number에서 number가 폴더 구조 중 []안에 들어간 변수명(number)
number: Number(router.query.number)
}
})
return (
<div>
<div>{router.query.number}번 게시글 이동이 완료되었습니다</div>
<div>작성자: {data?.fetchBoard.writer}</div>
<div>제목: {data ? data.fetchBoard.title : "불러오는중..."}</div>
<div>내용: {data ? data.fetchBoard.contents : "불러오는중..."}</div>
</div>
)
}
export default staticRoutedPage
JavaScript는 작성된 코드가 상단에서부터 순서대로 실행되기 때문에 데이터를 요청하고 응답을 받아오는 동안 화면에 그려질 데이터의 내용이 undefined이므로 첫 화면이 그려지는 시기에 데이터를 불러오면서 에러가 발생합니다.
이 부분이 효율적으로 실행되기 위해서 화면을 미리 그려놓고 데이터를 그려주기 위해서 조건부렌더링을 사용합니다.
조건부 렌더링에는 && 연산자, 삼향 연산자, 옵셔널 체이닝이 있습니다.
data는 동기적으로 받아와야하는 데이터입니다. 하지만 데이터가 오기 전에 이미 return 부분에서 rendering을 해주고 있기 때문에 삼향 연산자를 써서 데이터가 있을 때, 없을 때를 모두 적어줘야 합니다.
// data가 존재(true)하면 data.fetchProfile, 아니면(false) undefined
data ? data.fetchProfile : undefined
&& 연산자는 데이터가 없을 경우 자동으로 undefined를 반환해줍니다.
앞의 값이 참일 경우에만 뒤의 값을 보여줬는데, 반대로 앞의 값이 거짓일 때 뒤의 값을 보여주는 경우는 Nullish Coalescing 연산자라고 부릅니다.
?? 연산자는 앞의 값이 빈 값(true)이면 뒤의 값을 보여주며 || 연산자는 앞의 값이 거짓(false)일 경우 뒤의 값을 보여줍니다.
data ?? data.fetchProfile
data || data.fetchProfile
optional-chaining이란 기존의 && 연산자를 쓰면서 길어졌던 코드를 더욱 간결하게 사용하는 연산자입니다.
optional-chaining은 ES2020에서 나온 최신 문법입니다.
data?.fetchProfile
optional-chaining은 ? 연산자 앞 객체의 참조가ㅣ undefined || null이라면 undefined를 리턴해줍니다.
위의 있는 삼향 연산자, && 연산자와 똑같은 기능을 하는 것입니다.
// graphql 요청에 필요한 도구 불러오기
import { useQuery, gql } from '@apollo/client'
// graphql 코드 생성
const FETCH_PROFILE = gql`
query fetchProfile($name: String){
fetchBoard(name: $name){
title
contents
}
}
`
// graphql 코드를 실행하기 위한 query 생성과 생성된 query의 실행(자동으로 실행됩니다)
const { data } = useQuery(FETCH_PROFILE)
// 최종 결과를 HTML에 보여주기
<div>
이름: <span>{data && data.fetchProfile.name}</span>
나이: <span>{data && data.fetchProfile.age}</span>
학교: <span>{data && data.fetchProfile.school}</span>
</div>
템플릿 리터럴(Template Literal)
문자와 변수를 함께 쓸 수 있는 도구입니다. `${변수명}`으로 string과 변수를 연산자로 더해주지 않아도 가독성 좋게 코드를 작성할 수 있습니다.
mutation이 항상 성공하는 것은 아닙니다.
Back-end 컴퓨터에 문제가 발생할 수도 있고, 내가 수정하려는 게시물이 갑자기 삭제가 되는 바람에 수정에 문제가 발생하는 등 여러가지 이유로 실패의 가능성이 존재합니다.
따라서, 성공에 대한 처리와 실패에 대한 처리를 나누어 작업을 해야합니다.
try {
await createBoard({
variables: {
aaa: "훈이",
bbb: "1234",
ccc: "안녕하세요 훈이에요",
ddd: "반갑습니다"
}
})
} catch(error) {
alert(error.message) // 경고창(실패했습니다.) ==> 백엔드 개발자가 보내주는 실패 메시지
} finally {
// 성공, 실패 여부와 상관없이 무조건 마지막에 실행되는 부분
// 필요없다면 생략 가능
}
shorthand propety
객체를 정의할 때 객체의 key값과 value값이 같으면, 각각 표기하지 않고 한번만 표기하는 것을 의미합니다.const result = await createUseditem({ variables: { createUseditemInput: { name // name: myName remarks // remarks: myRemarks contents. // contents: myContents }, }, }, });
Apollo 디버깅 툴 (Apollo-Client-Devtools)
GraphQL을 사용하는데 도움을 주는 크롬 도구가 있습니다.
Apollo Client Devtools 설치하기