프론트엔드 - 4

송현섭 ·2023년 3월 19일

프론트엔드

목록 보기
5/24
post-thumbnail

Router 객체, Routing



router 객체

  • 페이지 이동과 관련된 기능을 가진 객체 (Next.js 에서 제공)

  • router 객체를 이용해서 A페이지에서 B페이지로 이동 => [B페이지로 라우팅 한다]





자주 사용하는 router 객체 기능


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/>
      </>
    )
  }







정적 라우팅




  • 정적 라우팅 = 항상 지정되어 있는 경로의 페이지로 이동하는 것

  • 위와 같이 페이지의 경로가 .../1, .../2 로 지정되어 있고, 함수가 실행되면 router.push( ) 내 경로를 따라 해당 폴더의 페이지로 이동 (정적 라우팅 하다)


    [BUT] => ex. 게시글이 100개가 넘는다면 각 게시글 페이지마다 폴더를 만들어서 라우팅 처리를 해줘야 함 (비효율적)





동적 라우팅


  • 정적 라우팅의 문제를 효율적으로 처리하기 위해 동적 라우팅 사용




동적 라우팅 사용하기


  • 위와 같이 라우팅 되는 페이지 하위 항목에 [] 로 감싸진 폴더를 새로 만들어 줌 (해당 폴더 이름은 변수명이기 때문에 다른 이름도 사용 가능)

  • [aaa] 에는 router.push( ) 로 이동시키고자 하는 경로의 마지막 부분의 값이 담김
    ex.
    -A페이지에서 router.push(05-04/../123) => [변수 aaa의 값은 123]
    -A페이지에서 router.push(05-04/../number) => [변수 aaa의 값은 number]






  • 라우팅되어 넘어간 페이지에서 console.log(router) 를 통해 query 객체 안의 aaa에 지정한 변수값이 담긴 것을 확인 가능




조회되는 query 내 aaa 변수 값 활용하기

  • 위에서 확인한 [aaa]의 값을 이용해서 A페이지에서 글을 등록하면 등록한 해당 글 페이지로 바로 이동되는 기능 구현 가능

// 글을 올리고 나면 올려진 해당 페이지로 이동 


import { useMutation, gql } from "@apollo/client"
import { useRouter } from "next/router"


const CREATE_BOARD = gql`   # $로 각각의 타입(type) 지정 [안 넣으면 오류남 (표시용)]
  mutation createBoard($writer: String, $title: String, $contents: String) {  
    # $로 표시하면 변수로 지정됨   
      createBoard(writer: $writer, title: $title, contents: $contents) {     
            _id
            number
            message
      }
    }
    `
   // 앞에 붙이는 gql => 백틱으로 감싼 mutation이 gql임을 알리는 키워드 




export default function GraphqlMutationPage() {


    const router = useRouter()

    const [createBoard] = useMutation(CREATE_BOARD)


    const onClickSubmit = async () => {

    try {

       const result = await createBoard({

        variables: {         
          writer: "현섭",
          title: "안녕하세요",  
          contents: "반갑습니다"
        }
       })
       console.log(result)  // mutation 함수에 변수로 입력한 값을 포함한 각 데이터가 담김(id, number, message...)
       console.log(result.data.createBoard.number)  // 이 데이터에서 number(게시글 번호)를 꺼내옴 

       router.push(`/section05/05-05-dynamic-routing-board-mutation-moved/${result.data.createBoard.number}`)
       // 경로 끝에 게시글 번호를 변수로 둬서 등록한 게시글로 바로 이동됨

    } catch(error) {

        alert(error.message)
    }


    }
  • A페이지에서 글을 작성하고 버튼을 누르면 onClickSubmit 함수가 실행되면서 내부의 createBoard 함수도 실행되어 글을 등록하는 API 요청이 전송됨

  • createBoard 함수를 result에 담아서 응답결과를 조회, 이를 통해 등록된 게시글의 글 번호를 확인하고, 이를 router.push( ) 경로 내 [aaa] 변수 값으로 적용
    *결과적으로 게시글을 등록할 때마다 [aaa] 에는 해당 게시글 번호가 담기게 됨




// 조회되는 게시글의 각 항목

import { useQuery, gql } 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 StaticRoutingPage() {


    const router = useRouter()

    console.log(router)

    Number(router.query.number) // 페이지 변수값으로 필요한 정보

    const {data} = useQuery(FETCH_BOARD, {
        variables: {
        number: Number(router.query.number) }
    })   // data는 처음에는 없는 값[undefined] (요청 후 응답을 받아서 데이터를 가져와야 data에 그 값이 할당됨)
                                           // 비동기이기 때문에 바로 밑의 return이 동작하고 data가 없을때 data.~~을 불러올 수 없음으로 에러 발생
                                           // 이후 백엔드에서 응답을 받고 data 값이 생기면서 data가 다시 재할당 됨
    return (
         <div>
            <div>2번 게시글 이동이 완료되었습니다</div>
            <div>작성자:{data && data?.fetchBoard?.writer}</div>  //엔드 연산자 (조건부 렌더링)
            <div>제목: {data?.fetchBoard?.title}</div>    // optionalChaining (조건부 렌더링)
            <div>내용: {data ? data?.fetchBoard?.contents : "로딩중입니다"}</div> //삼항 연산자 (조건부 렌더링)
         </div>
    ) 

}
  • [aaa] 변수 값에 따라 넘어간 페이지에 FetchBoard graphql API 코드를 작성하고 조회 시 이용되는 number의 값을 변수로 지정

  • const {data} =useQuery(FETCH_BOARD, {variables:{}}) 로 조회 API 요청 시 들어가는 number 변수 값으로 [aaa]의 값을 넣도록 지정, 그 응답 결과가 data 라는 변수에 담김


    *결과적으로 페이지가 라우팅될 때마다 라우팅 된 페이지는 조회하는 API의 number 값에 [[aaa]-게시글번호] 가 담겨서 해당 게시글을 조회하게 됨







조건부 렌더링

  • 위의 경우, useQuery로 데이터 조회를 요청하고 그 응답결과를 data라는 변수에 담음

  • API 요청이 비동기로 처리되기에 기다려지지 않고, 바로 return 문이 실행되어 처음에는 data에 값이 할당되지 않음(undefined) => 에러발생

  • 이후 요청에 응답을 받아오면서 data에 값이 할당됨(BUT return 문의 실행은 끝났기에 data 값이 실시간 업데이트 되지는 않음) => 에러 그대로 유지





[문제 해결] => 조건부 렌더링 사용


1. 삼항연산자

  • data ? data.fetchProfile : undefined
    [data 값 존재하면 data.fetchProfile 보여줌, 그렇지 않으면 undefined 반환]


2. && 연산자

  • data && data.fetchProfile
    [data 값 존재하면 data.fetchProfile 보여줌, 그렇지 않으면 undefined 반환]
    *&& 연산자는 값이 없을 때 자동으로 undefined 반환


3. Optional-Chaining

  • data?.fetchProfile
    [data 값이 undefined or null 이면 undefined 반환, 그렇지 않으면 data.fetchProfile 보여줌]



+a) Nullish coalescing

  • &&연산자와 반대되는 개념

  • data ?? data.fetchProfile
    [data 값이 null or undefined 이면 data.fetchProfile 보여줌, 그렇지 않으면 data 보여줌]




    or (||) 연산자 VS Nullish coalescing

    - or(||) 연산자 => 앞의 data 값이 falsey 할 때 뒤의 값 보여줌(0, null, undefined,"",...)

    -Nullish coalescing => 앞의 data 값이 Null, undefined 일 때만 뒤의 값 보여줌








graphql try ~ catch 적용


try {
		await createBoard({
				variables: {
						aaa: "훈이",
						bbb: "1234",
						ccc: "안녕하세요 훈이에요",
						ddd: "반갑습니다"
				}
		})

} catch(error) {
		alert(error.message)     // 경고창(실패했습니다.)  ==>  백엔드 개발자가 보내주는 실패 메시지
} finally {
	// 성공, 실패 여부와 상관없이 무조건 마지막에 실행되는 부분
	// 필요없다면 생략 가능
}
  • API 에 대한 요청이 항상 성공하는 것은 아님 (백엔드 컴퓨터 문제, 데이터의 갑작스런 변동 등)

  • 이에 따라 성공 처리, 실패 처리를 나누어서 작업해야 함

profile
막 발걸음을 뗀 신입

0개의 댓글