본문 바로가기
Web Study/Next.js

코딩애플 - Nextjs 로 게시판 글 작성하기

by 쿠리의일상 2023. 6. 25.

게시판 내용 목록 불러오기

먼저 /list 로 라우팅 처리를 해준다.

폴더 list 를 app 폴더 안에 만들고

-> list 폴더 안에 page.js 를 만들어준다.

 

해당 page.js 안에 몽고DB를 연동하고 게시글 목록을 불러와준다.

import {connectDB} from '@/utils/database'

const client = await connectDB;
const result = await client.db('forum').collection('post').find().toArray();

MongoDB를 connect 시켜둔 코드를 import 하고 client 에 db, collection 에 접근해준다.

잘 불러와진다.

 

그 다음으론 게시글의 상세페이지 이동을 처리 -> Dynamic Route

우선 [폴더명] 형태로 폴더를 만들어준다. -> page.js 를 만들어준다.

해당 의미는 폴더명으로 접근하는 url은 어떠한 다른 url로 들어와도 접근이 가능하게 해달라고 하는 약속이다.

예를 들어 위와같은 계층구조라면, detail/1 이든 detail/2든.... 모두 page.js 로 접근이 가능하다.

 

상세페이지마다 다른 내용 보여주기

위의 경우 다른 url 로 접근하더라도 별다른 처리가 없다면 모두 같은 내용이 뜨게된다.

이때는 위에서 작성해줬던 find() 메서드 대신 디비의 특정 object를 찾아올 수 있는 findOne() 메서드를 사용하여 찾아와준다.

const client = await connectDB;
const result = await client.db('forum').collection('post').findOne({ title : '안녕'});

이때 findOne() 의 매개변수로 위와 같이 중복될 수 있는 조건이 아닌, 객체를 한정 지을 수 있는 내용이 들어가는 게 맞기때문에

import { ObjectId } from 'mongodb'

const client = await connectDB;
const result = await client.db('forum').collection('post').findOne({ _id : new ObjectId('') });

몽고디비에서 기본적으로 주어지는 _id 값을 이용하여 접근하도록 한다.

이때 new ObjectId() 로 접근해줘야 한다.

 

그렇다면 게시글마다 다른 상세페이지를 불러오려면?

해당 Detail 컴포넌트 안에 props 속성을 넣어줘서 내용을 확인해본다.

detail/3 으로 접근했을 떄

params 안에 객체로 폴더명 : '접근한 url' 으로 확인해줄 수 있다.

export default async function Detail(props) {
  const client = await connectDB;
  const result = await client.db('forum').collection('post').findOne({ _id : new ObjectId(props.params.id)});
  console.log(props);

  return (
    <div>
      <h4>{result.title}</h4>
      <p>{result.content}</p>
    </div>
  )
}

props.params.폴더명 으로 접근해서 findOne() 처리를 해주면 각 페이지마다 몽고DB에 존재하는 게시글 내용을 확인 가능하다.

이때 없는 id값으로 접근하면 에러가 발생한다.

그리고 /list 페이지에서 해당 글과 글의 내용을 Link 로 연동시켜주면 게시글을 클릭했을 때 해당 게시글의 상세페이지로 넘어갈 수 있게 된다.

export default async function List() {
  const client = await connectDB;
  const boardList = await client.db('forum').collection('post').find().toArray();

  return (
    <div className="list-bg">
      {boardList.map((el, idx) => {
        return (
          <div className="list-item" key={idx}>
            <Link href={'detail/' + el._id}>
              <h4>{el.title}</h4>
              <p>{el.content}</p>
            </Link>
          </div>
        )
      })}
    </div>
  )
}

 

Link 태그 말고 페이지를 이동시키는 다른 방법

먼저 use client 로 새로운 컴포넌트를 만들어준다.

import { useRouter } from 'next/navigation'

-> useRouter() 훅을 사용해준다. 해당 훅은 클라이언트 컴포넌트 안에서만 사용이 가능하므로 서버 컴포넌트에선 사용불가함

-> 해당 훅을 변수에 저장해두고 메서드를 불러와서 사용해준다.

단순히 페이지 이동을 위해선 push() 메서드를,

export default function DetailLink(props) {
  const router = useRouter();
  
  return (
    <button onClick={() => {
      router.push(props.link);
    }}>이동</button>
  )
}
  • back() 메서드는 바로 이전 페이지로 
  • forward() 메서드는 앞으로 가기
  • refresh() 는 바뀐 내용만 새로고침해주기 (전부 새로고침X)
  • prefectch() 메서드는 페이지 미리 로드를 해줘서 사용자경험이 좋아질 수 있음
Link 태그는 이미 prefectch 기능이 내장되어 있으므로 페이지 로드가 미리 되어 있는 상태이다.
그렇기에 해당 태그를 남용하면 쓸데없이 페이지 로드가 되는 부분이 생기기 때문에
속성 prefetch 로 해당 기능을 on/off 가능하다.
<Link href={'detail/' + el._id} prefetch={false} > //...

 

그밖에도... usePathname() 과 useSearchParams(), useParams() 훅을 next/navigation 에서 불러와서 사용할 수 있음

  • usePathname() 은 현재 url 을 출력해주는 훅
  • useSearchParams() 은 쿼리스트링 내용을 출력해주는 훅
  • useParams() 는 dynamic route 입력한 내용을 출력해주는 훅

Link 태그 대신 이렇게 새로 클라이언트 컴포넌트를 만들어주면 위의 메서드처럼 여러 기능을 추가해줄 수 있는 이점이 존재한다.

 

3-tier architecture (3계층 구조) ?

플랫폼을 3계층으로 나눠서 별도의 논리적/물리적인 장치에 구축 및 운영하는 형태

데이터 입력/표현 계층(사용자 인터페이스)

- 프론트엔드 / GUI
데이터 처리하는 어플리케이션 계층

비즈니스 로직 계층 혹은 트랜잭션 계층

- 미들웨어 / 백엔드 / 서버
데이터를 저장하고 읽는 계층(DB)

- DBMS

글쓰기를 구현하려면 해당 원리에 맞게 해주는 것이 좋다.

 

기본적으로 서버 개발은?

XX 요청이 오면 ZZ 코드를 실행하게
/url
method -> Get/Post/Put/Delete/Patch
  • Method
    • Get : 유저에게 데이터 전송 시
    • Post : 새로운 데이터 추가 시
    • Put / Patch : 데이터 수정 시
    • Delete : 데이터 삭제 시

Nextjs 에서 서버 기능을 구현하려면,

  1. app 폴더 하위에 api 폴더를 만들어 주기 (강의 내용 상 이게 더 신버전이지만 아직 불안정하다고 함)
  2. app 폴더와 동등한 계층(최상위)에 pages 폴더를 만들고 그 하위에 api 폴더 만들어 주기

 

Nextjs 의 자동 라우팅 기능

이러한 계층 구조일 때, /api/test 로 요청을 하면 test.js 코드가 실행되게끔 자동 라우팅 기능이 존재한다.

// test.js
export default function handler(request, response) {
  console.log('Test!');
  
  // 서버는 기능 실행 후 유저에게 기본적으로 응답해줘야 하므로 아래처럼 response 파라미터로 return 해줘야한다.
  // 만약 return 해주지 않으면 로딩이 지속된다.
  return response.status(200).json('처리완료');
}

 

Get 요청하기

기본적으로 주소창에 url 을 입력하는 행위가 Get 요청이다.

Post 요청하기

가장 쉬운 방법은 form 태그를 사용하여 action 에는 url 주소를, method 에는 요청 메서드 post 를 입력해주는 것이다 (form 을 사용한 method 는 post와 get 밖에 안됨)

  <form action="/api/test" method="POST">
    <button type="submit">버튼</button>
  </form>

 

게시글 작성하는 기능 구현

기본적으로 몽고디비에 데이터를 삽입하는 명령어는 insertOne() 메서드이다. (객체 형태로 보내는 것을 추천)

그럼 특정 url 에서 정보를 post 한 request(요청)의 body에 접근하여 그 내용을 insertOne() 해주면 된다는 것이다.

      <form action="/api/test" method="POST">
        <input placeholder='제목' name='title' />
        <input placeholder='내용' name='content' />
        <button type="submit">버튼</button>
      </form>

form 태그 안에 input 태그로 써준 내용은 name 속성이 키값이 되어 request.body 에 담겨서 정보가 온다.

import { connectDB } from "@/utils/database";

export default async function handler(request, response) {
  const client = await connectDB;
  
  if(request.method == 'GET') {
    return response.status(200).json(new Date());
  } else if(request.method == 'POST') {
    await client.db('forum').collection('post').insertOne(request.body);
    return response.status(200).json('처리 완료');
  }
}

이러면 서버에 보내진 요청.body 안에 객체 형태로 제목과 내용이 삽입될 것이다.

response 에 redirect(statusCode, 'url') 형태로 써주면 강제 이동이 가능하다.