본문 바로가기

[mui][prisma][express][mysql] 게시판 만들기 - part 1

진행 과정

  1. 기획
  2. 버튼 생성
  3. 틀 구성
  4. table 작성
  5. post 객체 정의
  6. post 조회
  7. post 작성
  8. prisma-express 설정
  9. editor에 title 넣는 곳
  10. post 객체 재정의
  11. REST API 설계
  12. REST API 작성
  13. frontend - backend 연결 - (part 2 에서 계속)

 

기획

이동 방법:

- navbar 에 있는 햄버거 버튼 클릭 메뉴 중 게시판 주제 버튼 클릭 클릭

 

게시판 화면:

- 다른 사이트 참고

 

버튼 생성

생성 방법:

- 기존 mui 사용 (Menu 아래 MenuItem 하나 더 추가)

 

틀 구성

틀 위치:

- 테이블이 보여질 <div> 와 </div> 사이

 

버튼 액션:

- 분기에 사용할 boolean state 값 필요

 

  const [news, setNews] = useState(false);

      {
        news === true ? 
        <Box>

        </Box>
        :
        <Box>
        
        </Box>
      }

table 작성

테이블 틀:

- https://mui.com/material-ui/react-table/#system-BasicTable.js 참고

 

글 작성 버튼:

- https://mui.com/material-ui/react-button/#system-BasicButtons.js 참고

 

pagination 틀:

- https://mui.com/material-ui/react-table/#system-CustomPaginationActionsTable.js 참고

- Table Pagination component customize 가능

-- 예) 생성 버튼을 추가하고 싶으면, ActionsComponent 에 대응되는 component에 추가하면 됨

 

post 객체 정의

attributes:

model Post {
  id         Int        @id @default(autoincrement())
  createdAt  DateTime   @default(now())
  updatedAt  DateTime   @updatedAt
  title      String
  published  Boolean    @default(false)
  author     User       @relation(fields: [authorId], references: [id])
  authorId   Int
  categories Category[]
}

post 조회

틀 작성:

import * as React from 'react';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import Chip from '@mui/material/Chip';

const Post = ({ post }) => {
  return (
    <Card sx={{ margin: 2 }}>
      <CardContent>
        <Typography variant="h5" component="div">
          {post.title}
        </Typography>
        <Typography sx={{ mb: 1.5 }} color="text.secondary">
          Written by {post.author}
        </Typography>
        <Typography variant="body2">
          {post.content}
        </Typography>
        <Typography sx={{ mb: 1.5 }} color="text.secondary">
          Posted on {new Date(post.dateCreated).toLocaleDateString()}
        </Typography>
        <Typography sx={{ mb: 1.5 }} color="text.secondary">
          {post.views} views
        </Typography>
        <Typography sx={{ mb: 1.5 }} color="text.secondary">
          {post.likes} likes
        </Typography>
        <Box sx={{ '& > :not(style)': { m: 0.5 } }}>
          {post.tags.map((tag, index) => (
            <Chip key={index} label={tag} variant="outlined" />
          ))}
        </Box>
      </CardContent>
    </Card>
  );
};

export default Post;

데이터 전달:

<Post post={myPostData} />

 

post 작성

WYSIWYG editor 적용:

- open source 선택: 가장 쉬운 react-quill 사용

import React, { useState } from 'react';
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';

function MyComponent() {
  const [value, setValue] = useState('');

  return <ReactQuill theme="snow" value={value} onChange={setValue} />;
}

- action 설정:

"NEW" 버튼 클릭 시 editor가 호출 되도록 함

 

- POST 버튼 틀 작성:

추후 "POST" 버튼 클릭 시 작성한 내용이 서버에 저장 될 수 있도록

 

- CANCEL 버튼 틀 작성:

추후 "CANCEL" 버튼 클릭 시 작성 취소하고 되돌아 갈 수 있도록

prisma-express 설정

prisma-express 설치:

npx try-prisma@latest --template typescript/rest-express
cd rest-express
npm install

MariaDB 연결:

- prisma/schema.prisma 수정

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

- .env 수정

DATABASE_URL="mysql://yourid:yourpassword@localhost:3306/iamsolo_db"

- DDL 실행

CREATE USER 'yourid'@'localhost' IDENTIFIED BY 'yourpassword';
GRANT CREATE, ALTER, DROP, INSERT, UPDATE, DELETE, SELECT, REFERENCES, RELOAD, INDEX on *.* TO 'yourid'@'localhost' WITH GRANT OPTION;
CREATE DATABASE iamsolo_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

- dummy data 생성

npx prisma migrate dev --name init

- 서버 실행

npm run dev

post 객체 재정의

react-quill content 저장 시 데이터 구조:

- content 값은 delta라는 JSON으로 저장됨. 이 값을 db에 write하면 됨.

// const editorRef = useRef(null);
// <ReactQuill ref={editorRef} theme="snow" value={value} onChange={setValue} />

const getDataModel = () => {
  if (editorRef.current) {
    const quill = editorRef.current.getEditor();
    const delta = quill.getContents();
    return delta;
  }
  return null;
};

const dataModel = getDataModel();
const jsonData = dataModel ? dataModel.ops : [];

console.log(JSON.stringify(jsonData));

post 틀에서 필요한 정보:

- title

- author

- content

- dateCreated

- views

- likes

- tags

 

post 객체 재정의:

model Post {
  id         Int        @id @default(autoincrement())
  createdAt  DateTime   @default(now())
  updatedAt  DateTime   @updatedAt
  title      String
  content    String?
  published  Boolean    @default(false)
  views      Int        @default(0)
  likes      Int        @default(0)
  author     User?      @relation(fields: [authorId], references: [id])
  authorId   Int?
}

prisma model 작성:

- schema.prisma 수정

db update:

npx prisma migrate dev --name updated_post_object

REST API 설계

post 생성 API:

  • path: /post
  • method: POST
  • request body:
    • title
    • content
  • response body:
    • post 객체의 attribute. prisma 에서 자동 생성

post 조회 API:

  • path: /post
  • method: GET
  • request body: 없음
  • response body:
    • post 객체 list

REST API 작성

index.ts 수정:

app.post(`/post`, async (req, res) => {
  const { title, content } = req.body
  const result = await prisma.post.create({
    data: {
      title,
      content,
    },
  })
  res.json(result)
})
app.get(`/post`, async (req, res) => {
  const posts = await prisma.post.findMany()
  res.json(posts)
})