12.0 Models
포스트와 그 포스트에 대한 궁금증과, 답변, 궁금해요 모델 추가.

model Post {
  id         Int         @id @default(autoincrement())
  created    DateTime    @default(now())
  updated    DateTime    @updatedAt
  user       User        @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId     Int
  question   String      @db.MediumText
  answers    Answer[]
  wonderings Wondering[]

  @@index([userId])
}

model Answer {
  id      Int      @id @default(autoincrement())
  created DateTime @default(now())
  updated DateTime @updatedAt
  user    User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId  Int
  post    Post     @relation(fields: [postId], references: [id], onDelete: Cascade)
  postId  Int
  answer  String   @db.MediumText

  @@index([userId])
  @@index([postId])
}

model Wondering {
  id      Int      @id @default(autoincrement())
  created DateTime @default(now())
  updated DateTime @updatedAt
  user    User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId  Int
  post    Post     @relation(fields: [postId], references: [id], onDelete: Cascade)
  postId  Int

  @@index([userId])
  @@index([postId])
}

 

12.1 Forms and Handlers
12.2 Post Detail
12.3 궁금해요
12.4 Answer
12.5 All Posts

12.6 useCoords
12.7 Geo Search
12.8 Geo Bug


const posts = await client.post.findMany({
        include: {
          user: {
            select: {
              id: true,
              name: true,
              avatar: true,
            },
          },
          _count: {
            select: {
              wonderings: true,
              answers: true,
            },
          },
        },
        where: {
          latitude: { gte: parsedLatitude - 0.01, lte: parsedLatitude + 0.01 },
          longitude: {
            gte: parsedLongitude - 0.01,
            lte: parsedLongitude + 0.01,
          },
        },
      });

 

Type 'number' is not assignable to type 'null'.ts(2322)
해결 null as any로 바꾸면 된다는데

 
next.js에서는 페이지를 미리 만들어 놓기 때문에, 초기값으로 그려진다.

그래서 longitude, latitude가 null이었으니 제대로 동작 안하지. 

 

'클론코딩-캐럿마켓 > 전반' 카테고리의 다른 글

14 Streams - seeding  (0) 2023.01.11
13 Profile  (0) 2023.01.09
11 Products  (0) 2023.01.04
10 AUTHORIZATION - SWR  (0) 2023.01.04
9 AUTHENTICATION  (0) 2022.12.27

11.0 Product Model
hot reloading을 하면 일종의 서버 재시작이 돼서
PrismaClient가 계속 new로 생성됨.
DB에 커넥션 리밋이 있음. Planet Scale에 리밋이 있을 것이다.
좋지 않은 것이니. 고쳐

항상 다음 순으로 한다.
Model 생성 -> data base 수정 -> mutation -> 데이터 가져오기

Model 생성

model Product {
  id          Int      @id @default(autoincrement())
  created     DateTime @default(now())
  updated     DateTime @updatedAt
  user        User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId      Int
  image       String
  name        String
  price       Int
  description String   @db.MediumText

  @@index([userId])
}

 

11.1 Upload Form
11.2 Upload API

  const { name, price, description } = req.body;
  const { user } = req.session;
  // vs
  const {
    body: { name, price, description },
    session: { user },
  } = req;


Prisma client(back end)의 type을 갖다 쓸 수있다.

 

11.3 See Products
프로덕트에서 상품 정보와 상품등록한 계정의 정보 선택적으로 가져오기.

async function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseType>
) {
  console.log(req.query);
  const { id } = req.query;
  const product = await client.product.findUnique({
    where: {
      id: Number(id),
    },
    include: {
      user: {
        select: {
          id: true,
          name: true,
          avatar: true,
        },
      },
    },
  });
  res.json({ ok: true, product });
}

UI: skeleton 로딩화면 tailwind css로 만드는 법
https://www.section.io/engineering-education/skeleton-loading-in-nextjs-with-tailwindcss/

11.4 Product Detail
11.5 Related products

interface extends 해서 type만들기

prisma client의 search 조건
https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#filter-conditions-and-operators

Docs에 있는 것을 아래처럼 구현

 

 

11.6 Favorite Products

import { NextApiRequest, NextApiResponse } from "next";
import withHandler, { ResponseType } from "@libs/server/withHandler";
import client from "@libs/server/client";
import { withApiSession } from "@libs/server/withSession";

async function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseType>
) {
  const {
    query: { id },
    session: { user },
  } = req;
  const alreadyExists = await client.fav.findFirst({
    where: {
      productId: Number(id),
      userId: user?.id,
    },
  });
  if (alreadyExists) {
    await client.fav.delete({
      //delete하려면 unique 필드가 필요함
      where: {
        id: alreadyExists.id,
      },
    });
  } else {
    await client.fav.create({
      data: {
        user: {
          connect: {
            id: Number(user?.id),
          },
        },
        product: {
          connect: {
            id: Number(id),
          },
        },
      },
    });
  }
  res.json({ ok: true });
}
export default withApiSession(
  withHandler({
    methods: ["POST"],
    handler,
  })
);


11.7 Favorite Products part Two
Optimistic UI Update - 어차피 API성공할테니 응답 기다리지 말고 일단 UI바꾸기

  const [toggleFav] = useMutation(`/api/products/${router.query.id}/fav`);
  const onFavClick = () => {
    toggleFav({});
  };

toggleFav({})는 POST니까 바디 보내야 하지만 empty 바디 보내도 됨.


11.8 Bound Mutations

useSWR로 data와 mutate를 받을 수 있다.
mutate()의 처음 인자는 변경할 데이터, 두번째 인자는 해당 API를 재 호출 할지 선택하는 옵션이다. default는 true.
첫번째 mutate함수 처럼 사용하면, 기존의 swr 캐에 저장된 data가 인자 {product:{name:"potato"}}로 치환 되기 때문에 화면에 potato 관련 정보만 보이고 나머지는 다 날아가버린다.
그래서 두번 째 mutate처럼 써야한다.
원래 data를 전개해서 넣고, product안을 수정하는데 product에서도 나머지는 전개해서 넣고, name만 바꾼다.

이건 그냥 자바스크립트 문법.

다음 처럼 하면 favorite이 Optimistic UI Update 되는거야.
캐시만 변경하는거지.
mutate({ ...data, isLiked: !data.isLiked }, false);

두번째 인자가 false니까 다시 api 불러오지 않게
그리고 주석처리 된 toggleFave({})도 주석풀어야 실제 API콜이 될테니까.
최종본

그럼 왜 bound냐? 왜 bound mutation이냐?
useSWR로 받아온 mutate와 짝지어진 데이터만 변경 가능해서.
다른 request로 받은 놈의 cache도 바꾸고싶다?
다른 화면의 다른 요청의 cache를 바꾸고싶다?
unbound mutation !!

11.9 Unound Mutations

import useSWR, { useSWRConfig } from "swr";
const { mutate } = useSWRConfig();
mutate("/api/users/me", (prev) => prev && { ok: !prev.ok }, false);
//prev -> 현재 caching된 데이터 가져오기. 어디서든 접근 가능.
mutate("/api/users/me") //이렇게 하면 그냥 업데이트

 

'클론코딩-캐럿마켓 > 전반' 카테고리의 다른 글

13 Profile  (0) 2023.01.09
12 동네생활  (0) 2023.01.06
10 AUTHORIZATION - SWR  (0) 2023.01.04
9 AUTHENTICATION  (0) 2022.12.27
8 REFACTORING  (0) 2022.12.27

authentication = 유저가 누구인지 알아내는 것.
authorization = 유저가 권한이 있는지 알아내는 것.

10.0 Introduction
사용자가
incognito 모드로 접속하면 req.session.user?.id 사용 불가.
따라서 에러가 남. 이렇게 검증되지 않은 요청으로부터 핸들러를 보호해보자.

10.1 Protected Handlers

아래 코드 처럼 flag를 저렇게 넣는건 bad practice임. 
어떤것도 유추 할 수 없으니.

export default withApiSession(withHandler("GET", handler, true, false, true));

그래서 두개 이상의 arguments가 있으면 객체로 설정값을 보내라.

export default withApiSession(
  withHandler({
    method: "GET",
    handler,
    isPrivate: true,
  })
);

그럼 훨씬 self explanatory[ɪk│splænətɔːri] 하지.

10.2 useUser Hook
return router.push("/enter"); push는 페이지 이동이라 브라우저 history에 남음
return router.replace("/enter"); //남지않음
페이지가 성공적으로 로드 됐을 때만 히스토리에 남음

페이지마다 같은 api( 유저정보불러오기)를 다시 불러오면 좋지 않지.
메모리에 담아서 캐싱해. SWR을 써보자.

10.3 SWR (Stale-While-Revalidate = http 캐시 무효화 전략)
SWR은 캐싱된 데이터가 있으면 일단 화면을 그리고,
백그라운드에서 API call을 보내서 
데이터가 바뀐게 있는지 체크하고, 있으면 업데이트 한다.
SWR 설치
npm install swr

참고로 저렇게 중복 메시지가 나오는 이유는 리액트가 컴포넌트 2개 만들었기 때문. 그리고
react strict mode이기 때문. next.config.js에서 reactStrictMode를 false로 바꾸면됨.
근데 true해야 에러 더 잘 잡음.

useSWR 은 처음 인자로 url을 받는데 이를 key라고 하고, 두번째는 함수를 받고 fetcher라고 한다.
그래서 URL을 이용하여 fetcher함수를 통해 api call을 하고 data를 주고 에러가 났을 경우 error를 준다.

앞서 말한 key는 데이터의 id이기도 하다.
useSWR 안에는 super_cache라는게있다.

super_cache = {
	"/api/users/me" : {
        "ok": true,
        "profile": {
            "id": 10,
            "phone": "123456",
            "email": null,
            "name": "Anonymous",
            "avatar": null,
            "created": "2022-12-28T07:04:45.224Z",
            "updated": "2022-12-28T07:04:45.224Z"
        }
    }
}

이 super_cache는 앱 전체에서 공유되기 때문에
앱 어디서든 유저가 해당 url로 api을 하면 캐시를 준다!!!
당연히 업데이트 되면 스리슬쩍 바꿔주지 로딩 보여줄 필요 없이.
mutation도있는데 나중에 like 기능 있을 때보고.

useSWR은 유저가 다른 탭에 갔다가 돌아오면 데이터 새로고침 해줌. ㄷㄷ
아무도 모르게...

10.4 useUser Refactor
근데 매번 fetcher를 쓰는 건 번거로움
그래서 

const fetcher = (url: string) => fetch(url).then((response) => response.json());

그래서 _app.tsx에서 SWRConfig 컴포넌트를  불러와서 최상위 엘리먼트를 감싸면 됨!

'클론코딩-캐럿마켓 > 전반' 카테고리의 다른 글

12 동네생활  (0) 2023.01.06
11 Products  (0) 2023.01.04
9 AUTHENTICATION  (0) 2022.12.27
8 REFACTORING  (0) 2022.12.27
7 REACT HOOK FORM  (0) 2022.12.26

9.0 Introduction
authentication = 유저가 누구인지 알아내는 것.
authorization = 유저가 권한이 있는지 알아내는 것.

이 섹션에서 구현할 로직
1. Phone Number Sended to Backend --> User? Login : Sign in
2. Create Token -- Connect to User with Random Number
3. Enter Phone Number(Twillo) --> User receive Token(Random Number) by SMS
4. Enter Token --> Send Token to Backend --> Token? Log the user In!

9.1 Account Logic

findUnique는 User나 null 줄것이다.
upsert

string을 number로 바꾸려면 앞에 + 붙여주면 됨.
+"123" => 123
123 + "" => "123"

Update or create records (upsert)
upsert()는 기존 데이터를 업데이트하거나 새 데이터베이스 레코드를 생성합니다. 다음 쿼리는 upsert를 사용하여 특정 이메일 주소로 사용자 레코드를 업데이트하거나, 존재하지 않는 경우 해당 사용자 레코드를 생성합니다.
출처: 사용자 sugar댓글

VSCode의 block 주석 하는 법: Shift + Alt + A or 상단메뉴 Edit -> Toggle block comment

자동으로 if문 처럼 동작하
최종 shortened버전

 

9.2 Token Logic

아래 index는&nbsp;https://www.prisma.io/docs/concepts/components/prisma-schema/relations/relation-mode

9.3 Twilo Setup
9.4 Sending SMS

https://console.twilio.com/us1/develop/sms/try-it-out/send-an-sms?frameUrl=%2Fconsole%2Fsms%2Fgetting-started%2Fbuild%3Fx-target-region%3Dus1
npm i twilio
트윌로는 현재 node 16까지밖에 지원 안함.
그래서 nvm에서 노드 16으로 바꿔서 하니 잘 깔림.

 

느낌표 있으면 typescript한테 있는 타입이라고 말해주는 것.

9.5 Sending Email
npm install --save @sendgrid/mail
//아직 메일 인증되지 않아서 에러가 뜨는 것 같음
일단 넘어가자.

 

9.6 Token UI 

prisma stuio에서

User삭제하려고 하면 안됨.
User가 지워질 때, Token 물고있어서
그러면 어떻게 하냐?.( onDelete )
Cascade: 부모도 삭제해라.
SetNull: user값을 null로 둔다.
근데 여기선 그런 것은 필요없으니 Cascade로 두자.

구조분해
https://velog.io/@plu457/JavaScript-%EA%B5%AC%EC%A1%B0%EB%B6%84%ED%95%B4-%ED%95%A0%EB%8B%B9

 

9.7 Serverless Sessions
iron session을 써보자.
npm i iron-session


유저가 입력한 payload로 token을 검색하면
id:19인 튜플을 뽑아온다.

include라는 옵션을 주면, 
schema.prisma에서 정의한 릴레이션대로, 
user를 가지고 오는데 해당 모델(Token)의 userId로
참조하는 모델(User)의 id를 매칭한다.
매칭이 되면 이제
Token에 해당되는 User정보도 가져오는거지.


9.8 Profile Handler
9.9 Cleaning Code

9.10 NextAuth - 너무 매직같아서 안씀

 

'클론코딩-캐럿마켓 > 전반' 카테고리의 다른 글

11 Products  (0) 2023.01.04
10 AUTHORIZATION - SWR  (0) 2023.01.04
8 REFACTORING  (0) 2022.12.27
7 REACT HOOK FORM  (0) 2022.12.26
6 DATABASE SETUP  (0) 2022.12.26

8.0 Enter Form
별얘기없음

8.1 Form Submission
front에서 헤더를 정해주지 않으면,
req.body는 string으로 오게 됨. 그래서 
req.body.email 이런식으로 사용 못함
헤더에 Content-Type을 application/json으로 설정하면
req.body가 json형식으로 내려옴.
그래서 req.body.email 사용 가능.

 

8.2 Clean Code part One - 재밌다. fetch.then()이런거 정리 함.
8.3 Clean Code part Two 


8.4 widthHandler - 먼저 function을 어떻게 쓸지 적고 그 다음에 세부사항을 구현하는 방식이 좋다.
고차함수 = HOF(Higher Order Function) = 하나 이상의 함수를 인자로 받고, 결과로 함수를 리턴
https://medium.com/@la.place/higher-order-function-%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80-1c61e0bea79

 

8.5 Paths
import 예쁘게 하기
아래 안 예

import client from "../../../libs/server/client";
import withHandler from "../../../libs/server/withHandler";

 

1. tsconfig.json에서 baseUrl을 파입스크립트가 있는 위치(".")로해
2. paths에 @libs/* libs안의 모든 파일들, 이런식으로 정해
3. 그리고 tsconfig.json 설정 바꿨으면 서버 재시작 해야 

예쁘네

'클론코딩-캐럿마켓 > 전반' 카테고리의 다른 글

10 AUTHORIZATION - SWR  (0) 2023.01.04
9 AUTHENTICATION  (0) 2022.12.27
7 REACT HOOK FORM  (0) 2022.12.26
6 DATABASE SETUP  (0) 2022.12.26
4 TOUR OF TAILWIND -4 (Dark Mode, Just In Time Compiler )  (0) 2022.12.23

7.0 Introduction
일단 잠깐 쉬고 form만드는 짓을 해보자.
form은 너무 귀찮음.
React Hook Form package 써보자.


7.1 Making Forms Alone
npm i react-hook-form (React18)
npm i react-hook-form --legacy-peer-deps(React18이전)
일단 깔았고, 
이거 안깔고 하면 좋은 UX를 위해 할 일이 너무 많음을 보여줌.

7.2 The Register Function

//기존
export default function Forms(){
    const [username, setUsername] = useState("");
    const [email, setEmail] = useState("");
    const [password, setPassword] = useState("");
    const [formErros, setFormErros] = useState("");
    const onUsernameChange = (event:React.SyntheticEvent<HTMLInputElement>)=>{
        const {currentTarget: { value }} = event;
        setUsername(value);
    }
    const onEmailChange = (event:React.SyntheticEvent<HTMLInputElement>)=>{
        const {currentTarget: { value }} = event;
        setEmail(value);
    }
    const onPasswordChange = (event:React.SyntheticEvent<HTMLInputElement>)=>{
        const {currentTarget: { value }} = event;
        setPassword(value);
    }
    const onSubmit = (event:React.SyntheticEvent<HTMLFormElement>) =>{
        event.preventDefault();
        console.log(username,email,password)
    }
    return (
        <form onSubmit={onSubmit}>
            <input required minLength={5} value={username} onChange={onUsernameChange} type="text" placeholder="Username"/>
            <input required value={email} onChange={onEmailChange}  type="email" placeholder="Email"/>
            <input required value={password} onChange={onPasswordChange}  type="password" placeholder="Password"/>
            <input type="submit" value="Create Account"/>
        </form>
    );

}

 

input을 state에 연결해주는 것.


7.3 Validation

 

7.3 Validation part Two

가능한 validation options

7.3 Errors - Custom validation 만들기.

기본은 onSubmit, onBlur = 해당input exit하면 메시지 보여줌, onChange = input 바뀔 때마다

7.4 Extras
https://react-hook-form.com/api/useform

여기가면 많다.



// Wishes
// Less code (checked)
// Better valiation (checked)
// Better Erros(set, clear, display)
// Have full control over inputs
// Don't deal with events (checked)
// Eaiser Inputs (checked)

import { useForm } from "react-hook-form";
import { FieldError } from "react-hook-form/dist/types";

interface LoginForm {
    username: string,
    password: string,
    email: string,
    errors? : string,
}

export default function Forms(){
    const {
        register,
        handleSubmit,
        formState: { errors },
        watch,
        setError,
        setValue,
        reset,
        resetField,
    } = useForm<LoginForm>({mode: "onChange"});
    const onValid = (data: LoginForm) =>{
        console.log("I'm valid");
        // setError("errors", {message:"backend is offline sorry"})
        //fetch()같은거 하고 난 다음에
        // setError("username", {message:"Taken username"})
        // reset(); // 다 리셋하게
        resetField("password"); //password 필드만 리셋하게

    }
    const onInvalid = (errors: FieldError) =>{
        // console.log(errors)
    }
    // console.log(errors)
    // console.log(watch("email")) //하나 필드만 볼 수 있음. arg없으면 전체 봄.
    return (
        <form onSubmit={handleSubmit(onValid, onInvalid)}>
            <input
                {...register("username", {
                    required: "username is required",
                    minLength: {
                        message:"The username should be longer than 5 chars",
                        value: 5,
                    }
                })}
                type="text"
                placeholder="Username" 
            />
            {errors.username?.message}
            <input
                {...register("email", {
                    required: "email is required",
                    validate: {
                        notGmail: (value) => !value.includes("@gmail.com") || "Gmail is not allowed",
                    }
                })}
                type="email"
                placeholder="Email" 
                className={`${Boolean(errors.email) ? "bg-red-500" : ""}`}
            />
            {errors.email?.message}
            <input
                {...register("password", {
                    required: "password is required",
                })}
                type="password"
                placeholder="Password" 
            />
            <input type="submit" value="Create Account"/>
            {errors.errors?.message}
        </form>
    );
}

'클론코딩-캐럿마켓 > 전반' 카테고리의 다른 글

9 AUTHENTICATION  (0) 2022.12.27
8 REFACTORING  (0) 2022.12.27
6 DATABASE SETUP  (0) 2022.12.26
4 TOUR OF TAILWIND -4 (Dark Mode, Just In Time Compiler )  (0) 2022.12.23
4 TOUR OF TAILWIND -3 (Responsive Modifiers)  (0) 2022.12.23

5는 tailwind css라 건너 뜀
6.0 Before We Start
6.1 What is Prisma

Prisma란?
Node.js Typescript ORM(Object Relational Mapping)
번역기라고 보면 됨.
쿼리 없이 js로 DB 다룰 수 있게 해주는거.


6.2 Prisma Setsup
1. VSCode extension에서 Prisma 설치.
2. npm i prisma -D 설치
3. 명령어 바로 prisma하면안되고 npx prisma로 해야 함.
npx prisma init
그럼 prisma폴더와 .env 생김
아래 안내문구도 뜸. 저대로 해보자.

prisma/schema.prisma가서 provider를 mysql로 바꾸장

@같은 것들은 공식문서에 다 있다.

 

6.3 What is PlanetScale
serverless database platform = 데이터베이스 제공해준다.
serverless  = 서버를 우리가 유지보수 할 필요 없다는 뜻.
AWS의 RDS(관계형데이터베이스서비스)가 아님. 거기선 모든 설정 해야 함.
scale up/down다 해야 함.
MySQL-compatible = MySQL이 아님
그래서 설정을 좀 바꿔야 함. 나중에 보자.

MySQL이 아닌 이유는 얘네가 Vitess를 사용하기 때문.
Vitess는 유튜브를 scale하기 위해 구글이 만든 것
다시말해 Vitess는 대기업들이 규모에 맞게 MySQL을 scale하기 위해 쓰는 방법.
초당 수백만개의 쿼리, 수십만개의 연결, 수만개의 노드가 필요하면 scaling어렵다.
youtube,naver,slack,airbnb,twitter등에서 쓰임 ㄷㄷ
오픈 소스임.
planetscale의 가격:

planetscale은 개발자 경험이 좋음.
DB를 깃처럼 사용 할 수있다. 브랜치 따서 만들고..
downtime없이 배포 가능.
계정 만들자. 깃헙 있으면 그걸로 가입 가능.

6.4 Connecting to PlanetScale
여기 설명있음.
https://github.com/planetscale/cli

windows면 먼저 scoop설치해라.
https://scoop.sh/
파워쉘로 아래 명령 치면 됨.

> Set-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time
> irm get.scoop.sh | iex

근데 어드민으로 해서 잘 안되면(scoop에서 보안상 이슈로 ㅊㅊ 안함.) 
아래가서 해결 책을 봐라
https://github.com/ScoopInstaller/Install#for-admin

나는 아래 명령어 돌림

 

iex "& {$(irm get.scoop.sh)} -RunAsAdmin"

다시 planetscale, 아래 명령어 쳐주면 됨

scoop bucket add pscale https://github.com/planetscale/scoop-bucket.git
scoop install pscale mysql // pscale mysql 설치 함.

그래서 VSCode의 터미널에서 psacle을 쳐보면 잘나와야 하는데

에러가난다.

windows는 powershell로만 되나보다. 얼른 맥으로 갈아타고싶다.
혹시몰라서 vscode 껐다 키니까 된다.


pscale region list를 실행해보자. 근데 로그인 안돼있어서 안됨.
로그인 하자: pscale auth login
그럼 브라우저 창이 뜨고, 터미널의 Confirmation Code가 같은지 확인 하면 됨


다시 pscale region list 실행

ap-northeast에 DB만들어보자.

pscale database create carrot-market --region ap-northeast
carrot-market이란 DB를 해당 지역에 만듦. 
터미널에 링크 뜸.

컴퓨터와 PlanetScale 사이에보안 연결 어떻게 만들까?
  connect        Create a secure connection to a database and branch for a local client
pscale connect carrot-market
위 명령어 치면 로그인 된 내 계정의 DB에 연결이 됨.
따로 id,pw 저장 할 필요 없음.

연결됨

그럼 .env 파일에 다음과 같이 넣는다.

DATABASE_URL="mysql://127.0.0.1:3306/carrot-market"

그럼 보안 tunnel이 생기는 것.

6.5 Push To PlanetScale
PlanetScale(Vitess)은 MySQL compatible이기 때문에, 추가 설정이 필요함.
Vitess는 foregin key constraint가 없다.

예:
Users DB:
id: 1 username:nico
Comments DB:
id:1 text:wow! user:(Users DB:1)//객체주소알려주는거

id5인 사람(존재하지않는 사용자)이 만약 댓글 추가 하려면, 
작성자의 정보화 함께 댓글을 달려면 맨 처음 User DB에서 id가 5인 사용자가 존재하는지 확인함.
그렇기 때문에 Users DB에 반드시 있어야 함.(MySQL,PostgresQL 에서)

근데 Vitess는 가능함. DB 분화에 특화되어있음.
그래서 댓글 생성 전에 사용자가 존재하는지 확인하지 않음.
foreign key constraint를 지원하지 않음.
그래서 저렇게 저장해도 오류 일어나지 않음.
이는 좋은게 아니야
그래서 이런 기능이 없는 Vitess가 아닌 Prisma에서 체크를 해야 함.


relationMode = prisma하면 됨.

그러면 Data 스키마를 push해보자.
npx prisma db push

Prisma Client가 생성됨!
플래닛 스케일 가면 스키마 업로드 된거 확인 가능.

6.6 Prisma Client
npx prisma studio
실행하면 DB 관리자 페이지 보임.

이걸로 DB추가도 가능

 

일단 client를 추가해보자
npm i @prisma/client
-D를 안붙인 이유는 백엔드에서 직접 이 client를 사용할 것이기 때문
=서비스에서 사용한다.
lib폴더에 client.ts만들고 아래 내용 작성

import { PrismaClient } from "@prisma/client";
export default new PrismaClient();


그럼 prismaclient만든거야
그리고 다음처럼 해보자.
npx prisma generate

노드모듈에 설치 됨.
모듈 가서 보면 내가 친 DB 스키마가 typescript로 등록 되어 있음

 

user라는 우리가 정의한 테이블을 다룰 수 있다.

BUT 
그냥 brower에서 접근 가능한 저런 파일에서 DB를 직접 조작할 수 있는 소스를 쓰면
보안에 말도안되는 결점이 되겠지.
그래서 다행이도 에러가 뜸.
nextjs 프론트엔드에서 prisma를 임포트 할 수 없다.

그렇다면 이 client를 어디서 사용해야 하나?


6.7 API Routes
보통은 백엔드 서버를 따로 만들지
근데 NextJS에서 API만들기도 가능.
pages폴더에 api폴더 만들면 됨
NextJS서버에 API가 생긴거
규칙 가능단함.

그리고 실행시키고 플래닛스케일로 오면 데이터가 생김

 

6.7 Recap
npx prisma db push
npx prisma generate = 데이터베이스에 말걸 수 있는 client 생김

4.11 Dark Mode

dark:

darkMode를 class로 하면 code 설정에 따라 = 수동

 

부모에 dark하면 자식들이 dark된다. 즉 이게 수동적으로 수정 하는 법

수정으로 할거면 html이나 body 같은 최상위 요소에 dark 클래스 추가 하면 되는거지

4.12 Just In Time compiler
tailwind 3.0전에는,
purge 작업이 필요했음. 매우 큰 css 파일에서 사용하지 않는 css의 클래스 네임을 삭제 함.
근데 이젠 css파일이 빈 상태에서 시작함.
JIT 컴파일러 사용함으로써 실시간으로 사용하려는 클래스를 생성함.
그래서 클래스끼리 조합이 가능한거다.

많은 reset들이 있는데 아래 처럼 설정해서 그럼

그러나 tailwind의 가장 좋은점은!

tailerwind에서 벗어나고 싶으면
what if I want to break free from the restrictions of taliwind?
예전엔 다음 처럼 했었어야 한다.<h1 style={{fontSize:800}}/>

근데 이제 다음 처럼 됨
text-[98213px]
text-[#000]

쩌네

근데 배경화면 이미지 할 때가 최고다.
bg-[url('/vercel.svg')]

<div className="text-[948px] bg-[url('/vercel.svg')]"></div>

클래스 네임으로 css를 한다.

아 버스기사 아저씨 운전 너무 못한다. 토할거같네

'클론코딩-캐럿마켓 > 전반' 카테고리의 다른 글

7 REACT HOOK FORM  (0) 2022.12.26
6 DATABASE SETUP  (0) 2022.12.26
4 TOUR OF TAILWIND -3 (Responsive Modifiers)  (0) 2022.12.23
4 TOUR OF TAILWIND -2 (modifiers)  (0) 2022.12.23
4 TOUR OF TAILWIND -1  (0) 2022.12.23

+ Recent posts