윤스페이스 : 일상을 기록합니다.

이 포스팅은 https://blog.strapi.io/strapi-next-setup/ 에 있는 포스팅을 번역한 것 입니다.

오역이나 의역을 지적해주시면 확인 후 수정하겠습니다.


이 포스팅은 'Next.JS(React)와 GraphQL로 Deliveroo 클론하기'의 연재작 입니다.

Next.JS(React)와 GraphQL로 Deliveroo 클론하기 | 01. 🏗️ 기본 셋업

Next.JS(React)와 GraphQL로 Deliveroo 클론하기 | 02. 🏠 음식점 리스트 만들기

Next.JS(React)와 GraphQL로 Deliveroo 클론하기 | 03. 🍔 음식 리스트 만들기

Next.JS(React)와 GraphQL로 Deliveroo 클론하기 | 04. 🔐 회원 인증

Next.JS(React)와 GraphQL로 Deliveroo 클론하기 | 05. 🛒 장바구니 만들기


🍔 음식 리스트

음식점 리스트가 잘 표시되나요? 음식점 리스트 만들기는 첫번째 주요 단계였습니다. 축하드립니다. 

 

컨텐츠 정의하기

모든 음식점에는 음식을 판매하고 있으며, 음식 또한 데이터베이스에 저장해야 합니다. 저번에 만들었던 음식점 컨텐츠 타입과 같이 음식의 새로운 컨텐츠 타입을 정의하려 합니다. 저번에 만들어봤기 때문에 상세한 설명은 하지 않고 이름과 타입만 알려드리겠습니다.

 

  • name : 문자(String)
  • description : 텍스트(Text)
  • image : 미디어(Media)
  • price : 숫자(Number) - 정수(integer)
  • restaurant : 관계(Relation) 

관계 설정은 아래 사진을 참고해주세요.

 

항목 추가

저번 튜토리얼에서 항목을 추가하는 방법을 알아봤기에 추가적인 설명은 하지 않겠습니다. 음식점 별로 음식 데이터베이스를 추가해주세요. 많으면 많을수록 좋습니다.

 

음식 표시하기

URL의 restaurant id를 받아와서 음식을 표시해주려 합니다. /frontend/pages 디렉터리로 가서 restaurants.js 파일을 아래와 같은 코드로 만들어주세요.

import gql from "graphql-tag";
import { withRouter } from "next/router";
import { graphql } from "react-apollo";
import { compose } from "recompose";

import {
  Button,
  Card,
  CardBody,
  CardColumns,
  CardImg,
  CardSubtitle,
  CardText,
  CardTitle,
  Col,
  Row
} from "reactstrap";

class Restaurants extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    const {
      data: { loading, error, restaurant },
      router,
      context,
      isAuthenticated
    } = this.props;
    if (error) return "Error Loading Dishes";

    if (restaurant) {
      return (
        <>
          <h1>{restaurant.name}</h1>
          <Row>
            <Col xs="9" style={{ padding: 0 }}>
              <div style={{ display: "inline-block" }} className="h-100">
                {restaurant.dishes.map(res => (
                  <Card
                    style={{ width: "30%", margin: "0 10px" }}
                    key={res._id}
                  >
                    <CardImg
                      top={true}
                      style={{ height: 250 }}
                      src={`http://localhost:1337${res.image.url}`}
                    />
                    <CardBody>
                      <CardTitle>{res.name}</CardTitle>
                      <CardText>{res.description}</CardText>
                    </CardBody>
                    <div className="card-footer">
                      <Button outline color="primary">
                        + Add To Cart
                      </Button>

                      <style jsx>
                        {`
                          a {
                            color: white;
                          }
                          a:link {
                            text-decoration: none;
                            color: white;
                          }
                          .container-fluid {
                            margin-bottom: 30px;
                          }
                          .btn-outline-primary {
                            color: #007bff !important;
                          }
                          a:hover {
                            color: white !important;
                          }
                        `}
                      </style>
                    </div>
                  </Card>
                ))}
              </div>
            </Col>
          </Row>
        </>
      );
    }
    return <h1>Loading</h1>;
  }
}

const GET_RESTAURANT_DISHES = gql`
  query($id: ID!) {
    restaurant(id: $id) {
      _id
      name
      dishes {
        _id
        name
        description
        price
        image {
          url
        }
      }
    }
  }
`;
// The `graphql` wrapper executes a GraphQL query and makes the results
// available on the `data` prop of the wrapped component (RestaurantList)

export default compose(
  withRouter,
  graphql(GET_RESTAURANT_DISHES, {
    options: props => {
      return {
        variables: {
          id: props.router.query.id
        }
      };
    },
    props: ({ data }) => ({ data })
  })
)(Restaurants);

 

이제 localhost:3000에 접속해서 음식점을 클릭하면 추가한 음식 데이터베이스가 표시되어야 합니다.

제 경우에는 여기까지 정상적으로 렌더링이 되었으나, 원래는 express 서버가 필요하다 합니다. 아래의 코드를 터미널에서 실행시켜주세요.

> npm install express

 

루트 디렉터리에 server.js 파일을 아래와 같은 코드로 생성해줍니다.

const express = require('express')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare()
	.then(() => {
		const server = express()

		server.get('/restaurants/:id', (req, res) => {
			const actualPage = '/restaurants'
			const queryParams = { id: req.params.id }
			console.dir("req.params.id = " + JSON.stringify(req.params.id))
			app.render(req, res, actualPage, queryParams)
		})

		server.get('*', (req, res) => {
			return handle(req, res)
		})

		server.listen(3000, (err) => {
			if (err) throw err
			console.log('> Ready on http://localhost:3000')
		})
	})
	.catch((ex) => {
		console.error(ex.stack)
		process.exit(1)
	})

 

express로 서버를 구동시키기 위해 package.json 파일을 수정해주세요.

  "scripts": {
    "dev": "node server.js",
    "build": "next build",
    "start": "NODE_ENV=production node server.js"
  }

 

서버를 재시작 후 제대로 렌더링 되는지 확인해보세요. 정상적으로 표시된다면 express가 성공적으로 설치된 것입니다. 

 

다음에는 회원 인증을 구현해보려 합니다.

 

질문은 댓글로 남겨주시면 최대한 빠른 시간 안에 답변드리겠습니다.

 

포스팅 읽어주셔서 감사합니다 😁