본문 바로가기
> 코딩애플 (부분공개)/Next.js로 웹서비스 만들기

수정기능 만들기 2

by 자몽주스 2024. 11. 9.
728x90

저번시간 숙제로 내드린 기능을 완성해봅시다.

답안 따라쳐봤자 손가락만 아플 뿐이고

혼자 몇시간 부여잡고 검색하고 삽질해봐야 실력이 늡니다. 

실제 코딩도 검색하고 삽질하면서 하지 않습니까 


심심해서 Navbar 만들어봄

<div className="navbar"> 
  <Link href="/" className="logo">Appleforum</Link> 
  <Link href="/list">List</Link> 
</div>

layout.js에 이런거 추가했고

= Link 태그 쓰니까 상단에 import 필수. 

.navbar {
  background: white;
  padding: 20px;
}
.navbar a {
  margin-right: 10px;
  text-decoration: none;
  color: black;
}
.logo {
  font-weight: bold;
}

globals.css에 이런거 추가해서 상단바도 만들어봤습니다. 

수정기능 완성해오라던 저번시간 숙제를 해보자면 

전송버튼 누르면 DB에 있던 document를 작성내용으로 수정하면 된다고 했는데 

근데 유저가 DB 데이터를 직접 수정하면 큰일나니 서버에 수정을 부탁하는 식으로 코드짭시다. 

그럼 서버 기능부터 만들면 되겠군요. 


서버기능 만들기

서버에 DB수정하는 기능 만들고 

유저가 버튼누르면 그걸 실행하라고 부탁하는 식으로 코드짜면 됩니다.

수정페이지로 들어가보기. 

전송버튼도 만드어진 거 확인.

중간에 서버 만들어서 글 수정 부탁해줘야 함.

안그러면 위험. 

 

어떤 경로로 전송할 지

URL 과 메소드(겟 포스트 어쩌구) 하나 결정해주면 될듯? 

저렇게 action 속성에다가 작성해줌.

저렇게 작성해주면

저 URL 로 POST 요청이 갈 것. 

근데 유저가 입력한 input 데이터들도 함께 갈 듯.

 

이제 서버에서 기능 만들어주자.

누군가가 api/post/edit URL 로

POST 요청을 하면 특정 기능을 실행해달라고 코드를 짜기. 

 

그럴려면 저기다가 파일 하나 생성해줌.

전에 이거 했던 것 처럼. 

post 폴더 안에다가 file 하나 생성해줌. 

이렇게 edit 파일 만들어줬다. 

그리고 안에 코드 채워놓음 

안에 async 도 채워넣어줌 

= await 갖다 쓸거 같으니까. 

어떤 사람이 method 를 POST 로 요청했을 시 

안에 있는 코드를 실행하라고도 조건문 붙여줌. 

 

유저가 전송버튼을 눌러서 안의 데이터를 서버로 보내면

서버는 DB 에 있는 데이터를 수정해주면 될 것 같음. 

이런 식으로 쓰면 

DB 데이터를 수정해 준다고 함. 

소괄호 안에는 

두 개의 파라미터를 집어넣어야 함. 

첫 번째 파라미터: 어떤 파일 수정할 지 

두 번째 파라미터: 수정할 내용 

수정한 내용은

이런식으로 출력하게 되면

들어있는 거 확인 가능. 

= console.log(요청.body)

form 으로 전송했던 input 데이터들이 전부 들어가있을 것.

전송버튼 누르고 터미널로 확인해봤을 때

유저가 입력한 데이터가 전송 잘 되는 거 확인됨. 

그러면 요청.body 를 

두 번째 파라미터에가 집어넣은 모습

저 두번째 파라미터 자리에는

오브젝트 자료 형식으로 

수정할 내용을 집어넣게 돼있음 

요청.body 도 오브젝트 자료형 형식으로 출력이 됐음 

그래서 그냥 그대로 집어넣어주면 될듯. 

그리고 이젠 

어떤 파일을 수정할 지도 정해줘야함 

첫번째 파라미터엔 일반적으로 

저런 식으로 기입해줌. 

저렇게 써주면

id 가 123 인 도큐먼트를 찾아갖고

 

그 파일을 저 요청.body 에 있는 내용으로 덮어 씌워 줄 것.

근데 id 어디서 찾아올 수 있음? 

서버는 모룸 

이렇게 해주면 된다.

 

간단하게 유저한테 보내라고 해보기.

 

글 수정하고 싶으면

글 제목(title)하고 내용(content) 만 보내는게 아니라

글의 id 도 함께 보내라고 하기.

글 id도 함께 보내라고 하기 위해

input 을 하나 더 생성해줌. 

result 쓰고 _id 속성을 갖고오기위해 ._id 로 써줌

확인해보면 저런 식으로 id 도 잘 출력되는 거 확인할 수 있다.

근데 뒤에 toString 도 추가해줌

= _id 를 출력해보게 되면

DB에는 ObjectId() 가 겉에 쌓여져서

저장 돼있기 때문에 이걸 일반 문자로 바꿔주기 위해서 써줌. 

= 문자로 바꿔주기 위한 과정인 것. 

이제 전송버튼을 누르게되면

새로 생성한 input 도 서버로 전송될 듯. 

근데 유저가 id 를 맘대로 건드리면 어떻게할까? 

= style 속성으로 숨겨야함. 

style 속성 부여해서 숨겨줌.

이제 다시 edit.js (서버) 로 돌아가서

요청.body 를 다시 출력해보쟈 

터미널 확인하면 아까와 다르게

_id 가 추가돼서 나오는 거 확인

이제 첫번째 파라미터 채워줄 수 있음. 

근데 DB 에 있던 도큐먼트는 저런 식으로 저장돼있음.

objectId 함수 씌워주면 될 것 같음. 

= new 도 붙여주고 사용해줘야함. 

그리고 두 번째 파라미터도 좀 수정을 해야할 거 같음. 

이유: 요청.body가 아까랑 다르게 조금 바뀜 

= _id 속성도 추가됨.

_id 부분 빼서 집어넣어야 함.

새로운 변수하나 만들어서 새로운 object 자료를 하나 만들면 됨.

이렇게 생성해 준다음에

title 에다가는

요청.body 안에 있는 title 속성 집어넣어줌.

그리고 변수만 집어넣어주면 됨. 

진짜로 바뀌었는 지 결과를 확인해보고 싶으면 

result 출력해보면 됨. 

그리고 잘 바뀐 후에 응답을 해주고 싶은 경우

= 응답.status 한 다음에 저렇게 써주면 됨. 

connectDB 상단에 import 하는거 까묵지 말기.

updateOne 함수 관련 참고 사항

updateOne 함수 갖다 쓸 때 

바꿀거로 덮어써주세요 말고

다른 것도 가능 

document 에 저장된게 숫자일 경우

숫자를 덮어쓰는 게 아니라

기존 값 에서 1만 더해주고 싶은 경우

set 이 아니라 inc 라는 연산자를 활용하면 됨. 

= 기존값 증감. 


<div className="write">
  <form action="/api/post/edit" method="POST">
    <input name="title" defaultValue={result.title}/>
    <input name="content" defaultValue={result.content}/>
    <button type="submit">전송</button>
  </form>
</div>

action 속성에 이렇게 적으면 /api/post/edit.js 파일 안에 있는 코드가 실행되니까

그 파일 만들어서 기능만들면 되겠군요.

그래서 서버기능 만들러 api 폴더로 이동해봅시다. 

 

import { connectDB } from "@/util/database"
import { ObjectId } from "mongodb";

export default async function handler(요청, 응답) {
  if (요청.method == 'POST'){
    console.log(요청.body)
    let db = (await connectDB).db('forum')
    let result = await db.collection('post').updateOne({게시물정보}, { $set : {바꿀데이터}} );
    응답.redirect(302, '/list')
  }
}

pages/api/post/edit.js 파일 만들고 이렇게 코드를 짰습니다. 

유저가 폼으로 보낸 데이터는 요청.body 출력하면 있으니까 거기서

바꿀데이터 게시물정보도 꺼내서 저기에 잘 채워넣으면 될 것 같은데

 

하지만 요청.body 출력해보면 유저로부터 전달되는 데이터가

{title : 어쩌구, body : 어쩌구} 밖에 없습니다.

바꿀데이터는 있는데 게시물정보는 없는데유

<div className="write">
  <form action="/api/post/edit" method="POST">
    <input name="title" defaultValue={result.title}/>
    <input name="content" defaultValue={result.content}/>
    <input name="_id" defaultValue={result._id.toString()}/>
    <button type="submit">전송</button>
  </form>
</div>

그럼 유저에게 보내라고 하면 되는거 아니겠습니까. 

서버에서 코드짜다가 데이터같은게 없으면 유저에게 보내라고 하거나 DB에서 꺼내보면 됩니다. 

수정페이지에 <input>을 하나 더 만들고 _id를 집어넣어둡니다. 

그럼 서버로 _id 이름으로 ObjectId('어쩌구') 에서 '어쩌구' 내용부분만 전달됩니다.


Q. 왜 .toString() 쓰냐고요?

서버와 데이터 주고받을 때 문자, 숫자, JSON 이런 것만 주고받을 수 있습니다. 

저런 이상한 데이터들은 문자로 치환하는 방법을 찾아서 쓰면 되겠습니다. 

 

Q. _id 같은건 유저에게 안보여주는게 좋지않나요?

넴 display : none 같은 스타일 줘서 숨기면 됩니다. 


import { connectDB } from "@/util/database.js"
import { ObjectId } from "mongodb";

export default async function handler(요청, 응답) {
  if (요청.method == 'POST'){

    let 바꿀거 = {title : 요청.body.title, content : 요청.body.content}
    let db = (await connectDB).db('forum')
    let result = await db.collection('post').updateOne(
      {_id : new ObjectId(요청.body._id)}, 
      { $set : 바꿀거} 
    );
    console.log(result)

    응답.redirect(302, '/list')
  }
}

1. 요청.body에서 이것저것 꺼내서 바꿀데이터를 만들어서 updateOne() 안에 집어넣었습니다. 

2. 요청.body._id 꺼내서 게시물정보를 만들어서 updateOne() 안에 집어넣었습니다. 

{ } 자료같은게 어렵고 어색하면 자바스크립트 기초강의 들을 시점입니다.


(참고) 실은 GET, POST 말고도 PUT, DELETE 요청으로 서버와 통신할 수도 있습니다.

PUT은 수정, DELETE는 삭제할 때 쓰면 좋은데 서버 api들 끼리 이쁘게 구분하기 위함이고 필수는 아닙니다. 

단점은 <form>으로 데이터 전송시 PUT, DELETE는 사용할 수 없어서 굳이 쓰고 싶으면 외부 라이브러리 설치하거나 해야합니다.

그래서 코드가 너무 길거나 그런게 아니면 <form>으로 데이터 전송시엔 일단 POST 요청 쓰는게 낫습니다. 


오늘의 결론은

- document 수정하려면 updateOne 쓰면 됩니다. 값 변경말고 증가/감소도 가능함 

- 서버에서 코드짜다가 필요한 데이터가 없으면 1. 유저에게 보내라고 하거나 2. DB에서 꺼내써도 됩니다. 

728x90