[14:00 ~ 15:00]


it made me feel icky
께름칙하단건 알겠네요

ick·y
INFORMAL
unpleasantly sticky.
nasty or unpleasant.
"icky boys with all their macho strutting"
distastefully sentimental.
"a romantic subplot that is just plain icky"



you won't recognize yourself by the end of our time together.
우리가 각자 노선에 탈 때쯤에 당신은 딴사람이 돼있겟죠



Because that person you were lied to herself about who she was and what she wanted.
본인이 누구고 뭘 원하는지 자신마저 속일 사람요.


That was a person who became artsy and quirky and analytical when she was picked on as a kid instead of using what she really felt, which was hatred.

원래는 어릴 때 괴롭힘 당하며 느낀 감정도 삭였던 괴짜 분석가인데 말이죠
증오말이에요.

art·sy
INFORMAL
making a strong, affected, or pretentious display of being artistic or interested in the arts.
"the artsy town of Taos"

Someone who is arty seems very interested in drama , film, music, poetry , or painting . People often describe someone as arty when they want to suggest that the person is pretentious . artsy-craftsy. having to do with arts and crafts.

quirk·y
adjective
characterized by peculiar or unexpected traits.
"her sense of humor was decidedly quirky"


But it was impressed upon me that instead, and listen to me here, instead, it's best turned into fuel because it's effective.
제가 받은 가르침이긴 한데 잘 들어둬요.
And nothing burns as clean.
그보다 깔끔하게 타는건 없죠

16.0 Introduction
next.js에서는 img 태그 쓰지마라.
<Image> 써라.

import Image from 'next/image'
import profilePic from '../public/me.png'

function Home() {
  return (
    <>
      <h1>My Homepage</h1>
      <Image
        src={profilePic}
        alt="Picture of the author"
        // width={500} automatically provided
        // height={500} automatically provided
        // blurDataURL="data:..." automatically provided
        // placeholder="blur" // Optional blur-up while loading
      />
      <p>Welcome to my homepage!</p>
    </>
  )
}

이미지 import해서 변수처럼 쓰면된다.

https://nextjs.org/docs/basic-features/image-optimization

Improved Performance: Always serve correctly sized image for each device, using modern image formatsVisual Stability: Prevent Cumulative Layout Shift automatically
Faster Page Loads: Images are only loaded when they enter the viewport, with optional blur-up placeholdersAsset Flexibility: On-demand image resizing, even for images stored on remote server

 

16.1 Local Images
local image와 remote image 다루는게 다르다.
blurry 버전의 이미지 기능은 local image만 가능. 파일을 갖고있어야 함.
_next/image API Handler로 이미지 

http://127.0.0.1:3000/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Flocal.a1148338.jpg&w=3840&q=75
뒤의 Q는 퀄리티. 1~100까지.

blurry 이미지 적용하기  = placeholder에 blur적으면 됨.

<Image src={myImage} placeholder="blur" quality={100}/>

 

16.2 Remote Images
API로 받아오는 이미지 최적화 어떻게 하는지 알아보자.
nextjs는 이미지 서버를 물어본다.
next.config.js에 다음과 같이 작성.
imagedelivery.net은 cloudflare의 서버임.

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  images: {
    domains: ["imagedelivery.net"],
  },
};

module.exports = nextConfig;

근데 또 에러 뜸

Server Error

Error: Image with src "https://imagedelivery.net/0dNSIQsfAsyuUaSo7XjPgQ/undefined/public" is missing required "width" property.

16.3 Layout Fill
next.js는 이미지 사이즈를 알아야 한다. URL에 실제로 width넣을 필요 없음.
실제 화면에 보여질 사이즈만 알면 된다.

next.js는 내 서버에 이미지를 요청하고, 내가 태그에 적은 사이즈대로 이미지 생성 함.
그래서 실제 사용하는건 cloudflare에 있는게 아님.
로컬처럼 cloud 서비스 사용함.
https://nextjs.org/docs/api-reference/next/image#fill

<div className="relative pb-80">
    <Image
      src={`https://imagedelivery.net/0dNSIQsfAsyuUaSo7XjPgQ/${data?.product?.image}/public`}
      className="h-96 bg-slate-300 object-sacle-down"
      fill
    />
    <h1 className="absolute w-full text-center text-red-500">Hi there</h1>
  </div>

위처럼 relative주면 Image는 기본적으로 abolute라서 안보이게 할 수 있고,
위 태그 처럼 하면 배경이미지로 사용가능.
그리고 object-fit 사용하면 아주 좋음.
https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit

 

object-fit - CSS: Cascading Style Sheets | MDN

The object-fit CSS property sets how the content of a replaced element, such as an <img> or <video>, should be resized to fit its container.

developer.mozilla.org

근데 next.js 13부터는 좀 바뀜
https://nextjs.org/docs/api-reference/next/legacy/image#layout

 

next/legacy/image | Next.js

Backwards compatible Image Optimization with the Legacy Image component.

nextjs.org

16.4 Conclusions:
remote image 작업할 경우.블러 효과 할 수 없음.
blurDataURL= 이런식으로 가능... 근데 굳이.

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

17 LIVE STREAMING, 18 Code Challenge  (0) 2023.01.13
15 CLOUDFLARE IMAGES - 사진 다루기  (0) 2023.01.11
14 Streams - seeding  (0) 2023.01.11
13 Profile  (0) 2023.01.09
12 동네생활  (0) 2023.01.06

3.2 Polymorphism

// type SuperPrint = {
//     (arr: number[]):void
//     (arr: boolean[]):void
//     (arr: string[]):void
//     (arr: (number |boolean)[]):void
// }


// concrete type: number,boolean,string,void,
// generic은 플래이스 홀더


// 1. typestript에 generic사용한다고 말하라 함수 앞에.<Potato>.
// type SuperPrint = {
//     <T>(arr:number[]):void // 이 콜 시그니처가 제네릭을 받는다.
// }

// type SuperPrint = {
//     <T>(arr:T[]):void // 그리고 다음을 바꿔.
// }
const superPrint: SuperPrint = (arr) => arr[0]

type SuperPrint = {
    <T>(arr:T[]):T // 리턴을 해야 한다면 이렇게 바꿔. 그 타입 중 하나를 리턴한다는 것.
}

const a = superPrint([1,2,3,4])
const b = superPrint([false,true,true,false])
const c = superPrint([1,2,3,4])
const d = superPrint([1,2,true,false])

//generic으로 call signature를 바꾼다.

https://huchu.link/Dg5MP4o

 

3.3 Generic Recap

// type SuperPrint = {
//     <Type>(arr:Type[]):Type 
// }
// const superPrint: SuperPrint = (a) => a[0] //괄호가 없으면 바로 리턴한다.

// const a = superPrint([1,2,3,4])
// const b = superPrint([false,true,true,false])
// const c = superPrint([1,2,3,4])
// const d = superPrint([1,2,true,false])

//generic으로 call signature를 바꾼다.
// 그럼 any쓰면 되지않냐?
//d.toUpper
// a.toUpperCase()도 에러가 안나서 보호 못받음.

//타입을 하나 더 추가하고 싶다면?
type SuperPrint = {
    <T,M>(a:T[], b:M):T 
}
const superPrint: SuperPrint = (a) => a[0] //괄호가 없으면 바로 리턴한다.

const a = superPrint([1,2,3,4],"x")
const b = superPrint([false,true,true,false],"x")
const c = superPrint([1,2,3,4],"x")
const d = superPrint([1,2,true,false],"x")
console.log(d)
// 타입스크립트는 제네릭이 처음 사용되는 지점을 기반으로 이 타입이 무엇인지 알게 됨
// 순서가 중요하다.

https://huchu.link/wv4Y3yG

 

3.4 Conclusions

// type SuperPrint = {
//     <T>(a:T[]):T 
// }
// const superPrint: SuperPrint = (a) => a[0] //괄호가 없으면 바로 리턴한다.

// const a = superPrint([1,2,3,4])
// const b = superPrint([false,true,true,false])
// const c = superPrint([1,2,3,4])
// const d = superPrint([1,2,true,false])
// // 타입스크립트는 제네릭이 처음 사용되는 지점을 기반으로 이 타입이 무엇인지 알게 됨
// // 순서가 중요하다.

// // 근데 제네릭 만들 일은 거의 없을 꺼다.
// //사용만 하겠지.

type Player<E> = {
    name:string
    extraInfo:E
}
type NicoExtra = {
    favFood:string
}
type NicoPlayer = Player<NicoExtra>

const nico: Player<NicoExtra> = {
    name:"nico",
    extraInfo:{
        favFood:"kimchi"
    }
}

const lynn:Player<null> = {
    name:"lynn",
    extraInfo:null
}

https://huchu.link/hNtAumM


과제

  • 현재까지 배운 것을 토대로, 두 함수에 대한 구현과 함께 호출 시그니처(call signatures) 를 작성해주세요
  • last(arr): 이 함수는 배열의 마지막 요소를 반환해야 합니다.
  • prepend(arr, item): 이 함수는 배열의 시작 부분에 item을 넣고 return해야 합니다.
type SuperPrint = {
    <Type>(arr:Type[]):Type 
}
const last: SuperPrint = (a) => a[a.length-1] 

console.log(last([1,false,"last"]))

type Prepend = {
    <T,M>(a:T[],b:M):(T|M)[]
}

const prepend: Prepend = (arr,item)=> [item, ...arr]

const arr = prepend([1,2,3],"Good Morning!")
console.log(arr)


https://huchu.link/y9bNXg1

이제 훅 연습은 끝!!

15.0 Introduction
cloudfare images로 이미지를 추가해보자~
egress cost = 대역폭(bandwidth에 요금 내는거)
이미지 100,000개에 5달러.
이미지 resizing,최적화에도 돈 안받음.

15.1 Image Preview
blob: Binary Large Object
A binary large object is a collection of binary data stored as a single entity. Blobs are typically images, audio or other multimedia objects, though sometimes binary executable code is stored as a blob. Wikipedia
하나의 entity로 저장된 커다란 이진수 파일임. 주로 멀티미디어 객체가 되겠다.

<input
              {...register("avatar")}
              id="picture"
              type="file"
              className="hidden"
              accept="image/*"
            />

type을 file로 해서 아래처럼 url을 만들면

const file = avatar[0]; //이제 브라우저의 memory에 있겠지
console.log(URL.createObjectURL(file));
//브라우저가 만든 URL이 return됨

브라우저 메모리에 저장된 내가 올린 파일의 URL이 생김.
blob:http://localhost:3000/ead74686-349f-45fc-92d9-769f5ce35922
그럼 다른 브라우저에서는 될까? 안되야겠지?
해보니 안된다

브라우저에게 사용자가 올린 사진에 대한 엑세스를 URL을 통해 달라고 하는 것.
참고로 호출할 때마다 새로운 객체 URL을 생성해서 각각의 URL을 더는 쓰지 않을 땐 URL.revokeObjectURL()을 사용해서 해제해줘야 한단다.

15.2 Direct Creator Uploads
https://developers.cloudflare.com/images/cloudflare-images/upload-images/direct-creator-upload/

 API Token을 사용하는 방식을 이용했을 때
우리 서버가 있다면(우린serverless지)
유저가 올린 사진을 우리 서버에 올리고, 우리서버에서 cloudflare에 올려야 함.
서버가 있다면 이상한 방법이 아니긴 한데 우리 서버에 data가 왔다갔다 하는게 다 돈임.
그러니 유저 브라우저가 하게 하자.
유저에게 secure URL을 주고, 유저는 그 URL에 업로드하게 한다.
token같은거 유출 될일 없다.

Direct Creator Upload
작동원리:
1. 유저 사진 업로드 원함
2. 백엔드에서 key같은걸로 안전하게 CloudFlare(CF)에 URL달라고 요청
3. CF는 빈파일의  URL을 만들어서 백엔드에 주고 백엔드는 유저에게 .
4. 그럼 유저는 그 URL을 이용해 이미지를 direct로 올린다.

참고로 AWS에도 있다고 한다.
lambda 같이 써서 해야 한단다.
presigned url
ref: https://docs.aws.amazon.com/ko_kr/AmazonS3/latest/userguide/PresignedUrlUploadObject.html

 
15.2 Direct Creator Uploads
const cloudflareRequest = await fetch(`/api/files`);
const cloudflareURL = await cloudflareRequest.json();

//shortened
const cloudflareRequest = await (await fetch(`/api/files`)).json();​



15.3 Cloudflare Setup
15.4 Direct Upload URL
15.5 Cloudflare Upload

{
    "result": {
      "id": "2cdc28f0-017a-49c4-9ed7-87056c83901",
      "uploadURL": "https://upload.imagedelivery.net/Vi7wi5KSItxGFsWRG2Us6Q/2cdc28f0-017a-49c4-9ed7-87056c83901"
    },
    "result_info": null,
    "success": true,
    "errors": [],
    "messages": []
  }

 

 

uploadURL은 30분뒤 만료 되니 id만 BE에 저장하자.

file의 세번째 인자는 파일 이름.

15.6 Serving Images
15.7 Resizing Images
URL뒤에 variant(변형)을 둬서 미리 설정한 변형대로 리사이징 해줌.
15.8 Product Images


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

17 LIVE STREAMING, 18 Code Challenge  (0) 2023.01.13
16 NEXTJS IMAGES  (0) 2023.01.12
14 Streams - seeding  (0) 2023.01.11
13 Profile  (0) 2023.01.09
12 동네생활  (0) 2023.01.06

14.0 Upload Form
늘 하던 패턴의 반복.
Model -> Form(useForm()) -> useMutation() 
14.1 Detail Page
14.2 Send Message
14.3 See Message
14.4 Mutations and Refresh
14.5 Seeding - prisma의 seeding 가짜 데이트베이스 많이 생성 하는 법.

1. seed.ts파일 생성

seed.ts파일 생성

2. 한 유저가 많이 생성 한것처럼 코드짜고

import { PrismaClient } from "@prisma/client";
// next.js의 바깥쪽

const client = new PrismaClient();

async function main() {
  [...Array.from(Array(500).keys())].forEach(async (item) => {
    await client.stream.create({
      data: {
        name: String(item),
        description: String(item),
        price: item,
        user: {
          connect: {
            id: 13, //내 아이디
          },
        },
      },
    });
    console.log(`${item}/500`);
  });
}
main()
  .catch((e) => console.log(e))
  .finally(() => client.$disconnect());


3. npm i ts-node 설치(https://www.npmjs.com/package/ts-node)
이게 뭐냐? TypeScript execution and REPL for node.js, with source map and native ESM support.
REPL이란  : Node. js Read-Eval-Print-Loop (REPL) is an easy-to-use command-line tool, used for processing Node. js expressions. It captures the user's JavaScript code inputs, interprets, and evaluates the result of this code. It displays the result to the screen, and repeats the process till the user quits the shell.
브라우저 개발자 모드에 있는 console대신 쓸수 있는 듯

이거말하는듯

5. package.json맨 아래에 해당 명령어 추

6. 그리고 바로 npx prisma db seed 명령어 치면 에러 남.
SyntaxError: Cannot use import statement outside a module

7. 그럼 package.json에서 다음처럼 수정
참고: https://www.prisma.io/docs/guides/database/seed-database 

그리고 . npx prisma db seed다시 치면
데이터 400개쯤 만들다가 get kicked out 됨.
왜? planetscale은 무료로 1000개정도 커넥션 제공하는데 이건 문제가 안됨.
https://www.prisma.io/docs/concepts/components/prisma-client/working-with-prismaclient/connection-pool#external-connection-poolers

대신, pool time out 때문에 그렇다.
Pool은 데이터 모으는거고, pool timeout은 이게 정해진 시간 넘도록 특정 쿼리 처리 못하면 exception을 던지고 다음 쿼리로 감.
The default connection pool timeout is 10 seconds. If the query engine does not process a particular query within that time, it throws an exception and moves on to the next query in the queue.
근데 serverless가 아닌 일반 서버는 데이터베이스가 허용하는 연결의 수 제한이 매우 작음
근데 우리는 planetscale이 1000개의 동시연결이 가능하단걸 prisma에게 알려주지 않았다.
그래서 prisma에서 시간 초과가 되었다. 우린 일반 DB보다 훨씬 빨랐기 때문에!

연결 limit을 늘리거나, 커넥션이 더 길어져도 된다면 그걸 늘리면 됨.
schema.prisma 파일에  추가
https://www.prisma.io/docs/concepts/components/prisma-client/working-with-prismaclient/connection-pool#external-connection-poolers

 

datasource db { provider = "postgresql" url = "postgresql://johndoe:mypassword@localhost:5432/mydb?connection_limit=5&pool_timeout=2" }





14.6 Pagination
Try not to fry our database
아래처럼 포함하고 있는 관계에도 pagination할 수 있다. 
아니 무조건 해야 한다. 

다 돈이거든

 

prisma 로그 출력하는 방법


참고로 표준출력이란 stdout: https://ko.wikipedia.org/wiki/%ED%91%9C%EC%A4%80_%EC%8A%A4%ED%8A%B8%EB%A6%BC

SELECT `carrot-market`.`Stream`.`id`, `carrot-market`.`Stream`.`created`, `carrot-market`.`Stream`.`updated`, `carrot-market`.`Stream`.`name`, `carrot-market`.`Stream`.`description`, `carrot-market`.`Stream`.`price`, `carrot-market`.`Stream`.`userId` FROM `carrot-market`.`Stream` WHERE 1=1 ORDER BY `carrot-market`.`Stream`.`id` ASC LIMIT ? OFFSET ?

 

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

16 NEXTJS IMAGES  (0) 2023.01.12
15 CLOUDFLARE IMAGES - 사진 다루기  (0) 2023.01.11
13 Profile  (0) 2023.01.09
12 동네생활  (0) 2023.01.06
11 Products  (0) 2023.01.04

[13:00 ~ 14:00]


I thought you had your arms around this.
다 알아서 하신다더니요

*get/have one's arms around
INFORMAL
fully understand an issue or situation.
doctors are having difficulty getting their arms around these new findings"


I'm taking fucking measure.
대책을 강구중이에요.
*take measures
to take action; do things to accomplish a purpose


I saw the Kubrick fils as a tyke, turned me off'em.
어릴 때 큐브릭 영화에서 봤는데 영 거슬리더라.

*tyke : INFORMAL a small child.
*turn someone off
To cause someone to dislike, become averse to, or lose interest in something. A noun or pronoun can be used between "turn" and "off." Sometimes followed by "to (something)."
I thought the subject would be interesting, but the professor's boring lectures really turned me off.
I know that the accident really turned Janet off to driving on the highway.


Yes, true, I could tell you stories.
전 경험담도 있고요.


Not agains the wall, though.
총살은 안하실거죠?
(up) against a/the walll = placed against a wall to be executed by a firing squad
INFORMAL : in a very bad position or situation.
The team was up against a wall in the first half of the game.


We'll stop just short of that.
그전에 멈춰야지
short of = less than.
"he died at sixty-one, four years short of his pensionable age"

not reaching as far as.
"a rocket failure left a satellite tumbling in an orbit far short of its proper position"
without going so far as (some extreme action).
"short of putting out an all-persons alert, there's little else we can do"


 

이번 섹션은 구현의 반복

13.0 Models 

Review 모델은 User를 두번 가리켜야 한다.
내가 작성한 리뷰, 나에게 작성된 리뷰.
이러면 관계가 애매하다고 경고가 뜨는데
이때, relation에 name을 지어주면 된다.
그러면 user.writtenRiviews 이런 식으로 접근 가능.

모델만들기 -> 기능 만들기 -> 다른 모델 만들기 -> 다른 기능 만들기(x)
모델들 다 만들기 -> 기능 구현해나가기(o)

Enum - model의 모든 필드들이 같고 이름만 다를 때 사용
구조상 Purchase, Fav, Sale이 같음.
그럼 다음처럼 쓰면 된다.

SQL 레벨에선 enum이 저렇게 보인다.

 

13.1 Reviews
사용하던 모델(레코드를 생성했던) 새로운 필드를 추가하면 당연히 에러가 뜨겠지?

그러면 선택은 3가지

1. 기존 DB모두 삭제
2. 새로운 필드를 필수가 아닌 것으로 두기 score        Int?
3. 기본값 두기 score        Int      @default(1)

13.2 Handlers 
만약 Record라는 모델에 enum을 쓰는방식을 썼다면 다음 사진처럼 작성하면 됨.

13.3 Profile Page
13.4 Sales, Purchases, Favorites
13.5 Edit Profile part One
TODO
ENTER 페이지 빼고 모두 useUser사용해서 로그인 여부 체크해야 함.
로그인 이용자들만 볼 수 있게.
https://react-hook-form.com/api/useform/formstate
13.6 Edit Profile part Two

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

15 CLOUDFLARE IMAGES - 사진 다루기  (0) 2023.01.11
14 Streams - seeding  (0) 2023.01.11
12 동네생활  (0) 2023.01.06
11 Products  (0) 2023.01.04
10 AUTHORIZATION - SWR  (0) 2023.01.04

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

+ Recent posts