반응형
반응형

📝Generic

/** ---- Generic ---- **/
// function getSize(arr: number[] | string[])

// ---- same as above ----
function getSize<T>(arr: T[]): number{
    return arr.length;
}

const arr1 = [1,2,3];
console.log(getSize<number>(arr1)); // 3

const arr2 = ["a", "b", "c"];
console.log(getSize<string>(arr2)); // 3

const arr3 = [false, true, true]
console.log(getSize<boolean>(arr3)); // 3

const arr4 = [{}, {}, {"name" : "team"}]
console.log(getSize<object>(arr4)); // 3

interface User2 {
    nm: string;
    age: number;
}

interface Book {
    price: number
}

/** ---- Generic extends ---- **/
const user2: User2 = {nm : "lee", age : 20};
const book: Book = {price : 3000};

// Generic을 확장해 nm : string을 반드시 포함시키는 타입만 받는다는 의미
function showMyName<T extends {nm : string}>(data: T): string {
    return data.nm;
}

showMyName(user2); // lee
showMyName(book);  // 에러 발생 (semantic)

/** Generic 추가 예시 **/
// 사용 예시
class Container<T> {
    private data: T[];

    addItem(item: T): void {
        this.data.push(item);
    }

    getItem(index: number): T {
        return this.data[index];
    }
}

/** -------------------------------------- **/

// 사용 예시
let numberContainer = new Container<number>();
numberContainer.addItem(1);
numberContainer.addItem(2);
let firstItem = numberContainer.getItem(0); // firstItem의 타입은 number

interface Pair<T, U> {
    first: T;
    second: U;
}

let pair1: Pair<number, string> = { first: 1, second: "two" };
let pair2: Pair<string, boolean> = { first: "three", second: true };

/** -------------------------------------- **/

// 제네릭 인터페이스
interface Mobile<T> { 
   name: string;
   price: number;
   option: T; // 제네릭 타입 - option 속성에는 다양한 데이터 자료가 들어온다고 가정
}

// 제네릭 자체에 리터럴 객테 타입도 할당 할 수 있다.
const m1: Mobile<{ color: string; coupon: boolean }> = {
   name: 's21',
   price: 1000,
   option: { color: 'read', coupon: false }, // 제네릭 타입의 의해서 option 속성이 유연하게 타입이 할당됨
};

const m2: Mobile<string> = {
   name: 's20',
   price: 900,
   option: 'good', // 제네릭 타입의 의해서 option 속성이 유연하게 타입이 할당됨
};

/** -------------------------------------- **/

function identity<T extends numOrStr>(p1: T): T {
   return p1;
}

/** -------------------------------------- **/
function translate<T extends (a: string) => number, K extends string>(x: T, y: K): number {
   return x(y);
}

// 문자숫자를 넣으면 정수로 변환해주는 함수
const num = translate((a) => { return +a; }, '10');
console.log('num: ', num); // num : 10

/** -------------------------------------- **/

// 사용 예시
const toArray2 = <T>(a: T, b: T): T[] => { ... }
// 출처: https://inpa.tistory.com/entry/TS-📘-타입스크립트-Generic-타입-정복하기 [Inpa Dev 👨‍💻:티스토리]

 

📝Keyof, as

/** ------ keyof ------ **/

interface Rabbit {
    name: string;
    age: number;
    place: string;
    move: () => void;
}

type KeyofRabbit = keyof Rabbit
// "name" | "age" | "place" | "move"

const rabbit1: KeyofRabbit = "age";  // 🆗 OK!
const rabbit2: KeyofRabbit = "food"; // ⚠️ Error!

/** ------ as ------ **/

const someValue: any = "This Is As Test";
const strLength: number = (someValue as string).length;

console.log(strLength);


/** ------ as keyof ------ **/
week[checkbox.code] → checkbox.code가 어떤 타입인지 몰라서 에러 발생
week[checkbox.code as keyof Week] → as keyof로 명시가능

keyof인터페이스에 정의된 “키”를 literal type으로 변경한다

as의 경우 any따위로 두루뭉실하게 잡힌 타입을 “어떠한 타입이다”라고 임시로 정해준다

 

📝typeof

const person = { name: "Alice", age: 25 };
type Person = typeof person; // { name: string; age: number; } 타입

typeof변수를 넣을 경우 변수의 타입을 유추해서 type을 만든다

반응형
반응형

📝교차 타입

interface Car{
    name: string;
    start(): void;
}

interface Toy{
    name: string;
    color: string;
    price: number
}

const toyCar: Toy & Car = {
    name: "타요",
    start() {},
    color: "blue",
    price: 10000
}

 

📝접근 제한자 - static, public, private, protected (type-script에서만 지원)

class Car {

    static wheels: number = 4; // 전역변수 처리 가능

    //private color: string = "car";
    color: string // default : public
    constructor(color: string) {
        this.color = color;
    }

    start() {
        console.log("start");
    }
}

class BMW extends Car {
    constructor(color: string) {
        super(color);
    }

    showColor() {
        console.log(`super.color : ${super.color}`);
        console.log(`this.color :  ${this.color}`);
    }
}

const bmw = new BMW('red');
bmw.showColor();
// super.color : undefined
// this.color :  red
console.log(`Car.wheels : ${Car.wheels}`); // Car.wheels : 4
반응형
반응형

 

📝function

function liveDangerously(x?: number | null) {
  // No error
  console.log(x!.toFixed());
}

/** ---- function ---- **/
function hello(name?: string, age?: number, hobby?: string | undefined){
    return `Hello, ${name}. You are ${age}. and My Hobby is ${hobby}`;
}

console.log(hello("Lee", 30)); // Hello, Lee. You are 30. and My Hobby is undefined

// 예시2
// void 리턴인 경우 리턴 값이 있을 수도 없을 수도 있다
type voidFunc = () => void;

const f1: voidFunc = () => {
  return true;
};


/** ---- Function ---- **/
// Function은 함수계의 any이다
function doSomething(f: Function) {
  return f(1, 2, 3);
}

 

📝나머지 인자값

/** ---- rest parameters ---- **/
function add(...nums: Array<number>) {
    return nums;
}

console.log(add(1,2,3,4,5,6)); // [ 1, 2, 3, 4, 5, 6 ]

 

 

📝this

/** ---- this ---- **/
interface User {
    name: string
}

const Sam: User = {name:'Sam'}

function showName (this:User, age: number, gender: 'm'|'f') {
    console.log(this.name, age, gender)
}

const userInfo = showName.bind(Sam); // bind()는 this 값을 셋팅해주는 작업이다.
userInfo(10,'m'); // Sam 10 m

 

반응형
반응형

 

📝리터럴

/** ---- 리터럴 ---- **/
function printText(s: string, alignment: "left" | "right" | "center") {
  // ...
}
printText("Hello, world", "left");

// 예시2
interface Options {
  width: number;
}
function configure(x: Options | "auto") {
  // ...
}
configure({ width: 100 });
configure("auto");

리터럴로 선언한 값만 사용 가능

 

📝유니온 타입

/** ---- 유니온 타입 ---- **/
function printId(id: number | string) {
  console.log("Your ID is: " + id);
}

printId(101); // OK
printId("202"); // OK

두개의 타입을 허용합니다

 

📝익명 함수

/** ---- 익명 함수 ---- **/
interface Minus {
    (num1 : number, num2 : number):number;
}

const minus: Minus = (x, y) => {
    return x - y;
}

console.log(minus(1,5)); // -4

 

📝Interface

/** ---- interface ---- **/
// 인터페이스는 ES6가 지원하지 않는 타입스크립트만의 특징으로 JS컴파일 후에 사라집니다.
interface Car {
    color: string;
    wheels: number;
    start() : void;
}

/** ---- interface extend interface ---- **/
// interface끼리 확장은 가능하지만 Java처럼 Interface 그 자체로는 사용을 못한다
interface Stuff {
    color: string
}

interface Mouse extends Stuff {
    name: string
}

 

📝Class

/** ---- class extends class ---- **/
class Cup {
    color: string;
    constructor(color: string) {
        this.color = color;
    }

    sayMyName(): void{
        console.log(this.color);
    }
}

class GlassCup extends Cup{

    name: string;

    constructor(color: string, name: string) {
        super(color);
        this.name = name;
    }
    sayMyName() {
        console.log("glassCup");
    }
}

let glassCup = new GlassCup("blue","france cup")

console.log(`glassCup.name : ${glassCup.name}`) // glassCup.name : france cup
glassCup.sayMyName(); // glassCup
  • 클래스의 경우 constructor가 필요하다 그렇지 않으면 에러 발생한다
  • this의 경우 클래스 안의 변수를 찾아간다 this를 사용하지 않으면 class 밖에 있는 변수를 찾아간다

 

Getter, Setter

class C {
  _length = 0;
  get length() {
    return this._length;
  }
  set length(value) {
    this._length = value;
  }
}

getter와 setter를 선언할 수 있다

 

 

오버라이드

class Base {
  greet() {
    console.log("Hello, world!");
  }
}
 
class Derived extends Base {
  greet(name?: string) {
    if (name === undefined) {
      super.greet();
    } else {
      console.log(`Hello, ${name.toUpperCase()}`);
    }
  }
}
 
const d = new Derived();
d.greet(); // Hello, world!
d.greet("reader"); // Hello, READER

오버라이드할 때 상위 클래스의 형태를 포함하고 있지 않으면 에러가 발생한다 위의 코드의 경우는 optional처리가 되어있어서 상관 없지만 그렇지 않으면 에러 발생합니다

 

 

public, protected, private, static

/** public **/
class Greeter {
  public greet() {
    console.log("hi!");
  }
}
const g = new Greeter();
g.greet();

/** protected **/
class Greeter {
  public greet() {
    console.log("Hello, " + this.getName());
  }
  protected getName() {
    return "hi";
  }
}
 
class SpecialGreeter extends Greeter {
  public howdy() {
    // OK to access protected member here
    console.log("Howdy, " + this.getName());
  }
}
const g = new SpecialGreeter();
g.greet(); // OK
g.getName();
// Property 'getName' is protected and only accessible within class 'Greeter' and its subclasses

/** private **/
class Base {
  private x = 0;
}
const b = new Base();
// Can't access from outside the class
console.log(b.x);
// Property 'x' is private and only accessible within class 'Base'.

/** static **/
class MyClass {
  static x = 0;
  static printX() {
    console.log(MyClass.x);
  }
}
console.log(MyClass.x);
MyClass.printX();
  • public의 경우 외부에서 코드를 제어할 수 있습니다
  • protected외부에서 코드를 제어할 수 없고 extends했을 때는 해당 클래스 내부에서는 사용이 가능합니다
  • privateprotected랑 비슷하지만 하위 클래스에서도 멤버에 대한 엑세스 허용이 되지 않습니다
  • static의 경우는 클래스의 객체를 만들어서 쓰는게 아니라 클래스 자체를 수정할 수 있습니다

 

📝Abastract Class

class Derived extends Base {
  getName() {
    return "world";
  }
}
 
const d = new Derived();
d.printName();

class Derived extends Base {
//Non-abstract class 'Derived' does not implement inherited abstract member 'getName' from class 'Base'.
  // forgot to do anything
}

추상 클래스의 경우 상속받아서 사용하며 추상 메서드는 구현이 필요하다

 

📝Class Implements Interface

/** ---- class implements interface ---- **/
class BMW implements Car{

    color;
    wheels = 4;
    constructor(color: string) {
        this.color = color;
    }

    start(){
        console.log("go....");
    }
}

const bmw = new BMW('green');
bmw.start(); // go....
console.log(bmw); // BMW { wheels : 4, color : 'green' }

/** ---- class implements interface(2) ---- **/

interface A {
  x: number;
  y?: number;
}
class C implements A {
  x = 0;
}
const c = new C();
c.y = 10;
// Property 'y' does not exist on type 'C'.

인터페이스를 상속받을 때 Optional인 경우 해당 속성은 생성되지 않습니다

 

 

📝Type

/** ---- Type ---- **/
type Animal = {
  name: string;
}

/** ---- Type Extension ---- **/
type Bear = Animal & { 
  honey: boolean;
}

const bear = getBear();
bear.name;
bear.honey;


/** ---- Type vs Interface ---- **/
// interface 기능을 Type이 대부분 사용 가능하지만 Type은 재선언이 불가능
interface Window {
  title: string;
}

interface Window {
  ts: TypeScriptAPI;
}

type Window = {
  title: string;
}

type Window = {
  ts: TypeScriptAPI;
}

 // Error: Duplicate identifier 'Window'.

 

 

🔗 참고 및 출처

https://www.typescriptlang.org/docs/handbook/2/everyday-types.html

반응형
반응형

📝any

어떤 유형이든 들어갈 수 있는 타입이다 특정한 경우 외에는 타입스크립트의 존재의의와 반발하기 때문에 추천하지 않는다

 

📝No Implicit Any

유형을 지정하지 않고 TypeScript가 컨텍스트에서 유형을 유추할 수 없는 경우 컴파일러는 일반적으로 any로 둔다 하지만 일반적으로 는 유형 검사가 되지 않으므로 타입스크립트에서는 No Implicit Any를 일으켜서 오류를 일으킵니다

 

📝Strict Null Checks

Null과 Undefined에 대해서 에러를 체크해 표시합니다

 

 

📝 논리, 문자, 숫자, 배열, 튜플, Enum 

/** ---- 숫자 및 문자 ---- **/
function add(num1: number, num2: number) {
    console.log(num1 + num2);
}

add(5,10);  // ✅️ 정상 동작
add(5,"뮤"); // 에러 발생 (semantic)

/** ---- 배열 ---- **/
function showItem(arr: number[]) {
    arr.forEach((item) => {
        console.log(item);
    })
}

showItem([1,3,5]); // ✅️ 정상 동작
// showItem();         // 에러 발생 (compile)
// showItem(["hello"); // 에러 발생 (semantic)

/** ---- 튜플 ---- **/
let b:[string, number];
b = ['z', 1];          // ✅️ 정상 동작
// b = ['z', 1];       // 에러 발생 (semantic)
console.log(b);

/** ---- enum ---- **/
enum Os {
    Window = 'win',
    Ios = 'ios',
    Android = 'and'
}

let myOs :Os;
myOs = Os.Window;
console.log(myOs); // ✅️ 정상 동작

myOs = "str window"; // 에러 발생 (semantic)

/** ---- enum (2) ---- **/
enum Enum {
  A,
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"


/**
 * -- 타입 체크 방법
 *  논리                : boolean
 *  문자                : string
 *  숫자                : number
 *  문자 배열            : string[] = Array<string>
 *  숫자 배열            : number[] = Array<number>
 *  튜플                : [string, number]
 *  void               : void
 *  Error or Never End : never
 *  undefined          : undefined
 *  null               : null
 *  enum               : enum
 *  등 ...
 */

 

🔗 참고 및 출처

https://www.typescriptlang.org/docs/handbook/2/basic-types.html

반응형
반응형

const keywordEnrollOption: radio[] = [
  { value: Code.A, id: 'auto_enroll', label: '자동등록' },
  { value: Code.M, id: 'manual_enroll', label: '수동등록' },
];

export interface radio {
  value: string;
  id: string;
  label: string;
}
const RadioGroups = ({
  onValueChange,
  radios,
}: {
  onValueChange: RadioGroupContextValue['onValueChange'];
  radios: radio[];
}) => {
  return (
    <>
      <RadioGroup
        defaultValue={radios[0].value}
        className="flex"
        onValueChange={(value) => onValueChange(value)}
      >
        {radios.map((item) => (
          <div key={item.id} className="flex items-center space-x-2">
            <RadioGroupItem value={item.value} id={item.id} />
            <Label htmlFor={item.id}>{item.label}</Label>
          </div>
        ))}
      </RadioGroup>
    </>
  );
};

/** 컴포넌트 사용 **/
const dispatch = useAppDispatch();
const setKeywordStrategy = (value: string) => {
    dispatch(groupInfoSlice.actions.setKeywordStrategy(value));
};
  
<RadioGroups
    onValueChange={setKeywordStrategy}
    radios={keywordEnrollOption}
/>

RadioGroup 컴포넌트의 경우 https://ui.shadcn.com/에서 가져온 거라 그 부분은 본인이 만든 라디오 컴포넌트에 맞게 만드시면 됩니다

리덕스로 상태관리를 했으며 useState로 관리하는 것도 컴포넌트 사용에서 넘겨주는 Setter를 변경해서 넘겨주면 됩니다

반응형
반응형

export interface checkbox {
  text: string;
  code: string;
}

const weekOption: checkbox[] = [
  { text: '일', code: 'week1' },
  { text: '월', code: 'week2' },
  { text: '화', code: 'week3' },
  { text: '수', code: 'week4' },
  { text: '목', code: 'week5' },
  { text: '금', code: 'week6' },
  { text: '토', code: 'week7' },
];

const Checkboxes = ({
  checkboxOption,
  disabled,
}: {
  checkboxOption: checkbox[];
  disabled: boolean;
}) => {
  const [checkbox, setCheckbox] = useState<string[]>([]);

  const addCheckboxOption = (checked: CheckedState, value: string) => {
    let updatedOption: string[];

    if (checked) {
      updatedOption = [...checkbox, value];
    } else {
      updatedOption = checkbox.filter((el) => el !== value);
    }

    // checkboxOption에 적힌 순서대로 정렬 해서 추가
    const sortedCheckbox = checkboxOption.filter((checkbox) =>
      updatedOption.includes(checkbox.code),
    );

    setCheckbox(sortedCheckbox.map((checkbox) => checkbox.code));
  };

  return (
    <>
      {checkboxOption.map((checkbox) => (
        <React.Fragment key={checkbox.code}>
          <Checkbox
            id={checkbox.code}
            checked={disabled ? disabled : undefined}
            disabled={disabled}
            onCheckedChange={(e) => addCheckboxOption(e, checkbox.code)}
          />
          <label
            htmlFor={checkbox.code}
            className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
          >
            {checkbox.text}
          </label>
        </React.Fragment>
      ))}
    </>
  );

Checkbox박스 컴포넌트의 경우 https://ui.shadcn.com/에서 가져온 거라 그 부분은 본인이 만든 체크박스 컴포넌트에 맞게 만드시면 됩니다

 


 

interface CheckBoxActions {
  [key: string]: ActionCreatorWithPayload<string[], string>;
}

const checkboxActions: CheckBoxActions = {
  groupWeek: groupInfoSlice.actions.setExposureWeek,
  groupExposureArea: groupInfoSlice.actions.setExposureArea,
};

type SelectorFunction = (state: RootState) => string[];

const stateSelectors: Record<string, SelectorFunction> = {
  groupWeek: (state) => state.groupInfo.exposureWeek,
  groupExposureArea: (state) => state.groupInfo.exposureArea,
};

const Checkboxes = ({
  checkboxOption,
  actionName,
  disabled,
}: {
  checkboxOption: checkbox[];
  actionName: string;
  disabled: boolean;
}) => {
  const dispatch = useAppDispatch();
  const action = checkboxActions[actionName];

  const checkbox = useSelector((state: RootState) => {
    const selector = stateSelectors[actionName];
    return selector ? selector(state) : [];
  });

  const addCheckboxOption = (checked: CheckedState, value: string) => {
    let updatedOption: string[];

    if (checked) {
      updatedOption = [...checkbox, value];
    } else {
      updatedOption = checkbox.filter((el) => el !== value);
    }

    // 정렬 추가
    const sortedCheckbox = checkboxOption.filter((checkbox) =>
      updatedOption.includes(checkbox.code),
    );

    const result = sortedCheckbox.map((checkbox) => checkbox.code);
    dispatch(action(result));
  };

  return (
    <>
      {checkboxOption.map((checkbox) => (
        <React.Fragment key={checkbox.code}>
          <Checkbox
            id={checkbox.code}
            checked={disabled ? disabled : undefined}
            disabled={disabled}
            onCheckedChange={(e) => addCheckboxOption(e, checkbox.code)}
          />
          <label
            htmlFor={checkbox.code}
            className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
          >
            {checkbox.text}
          </label>
        </React.Fragment>
      ))}
    </>
  );

여기는 전역 상태관리 Redux를 사용한 경우 컴포넌트를 전역 상태관리할 수 있게 나름대로 코드를 만들어봤습니다

 

  • actionName 상태관리 변수명에 따라 useSelector로 값을 가져오며 상태관리 변수명에 따라 값을 Set할 수 있게 action을 설정해줍니다
  • checkboxActions, stateSelectors에서 actionName에 대한 "키"를 입력해주면 됩니다

 

반응형
반응형

 

prisma/schema.prisma

model User {
  id    Int     @id @default(autoincrement()) // pk, autoincremnt속성
  email String  @unique // unique속성
  name  String? @db.VarChar(255)
  posts Post[] // 1:N 관계 설정
}

model Post {
  id        Int     @id @default(autoincrement())
  title     String
  content   String?
  published Boolean @default(false) // default값 설정
  author    User    @relation(fields: [authorId], references: [id]) // fields : fk키, references : 연결 테이블의 키
  authorId  Int
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

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

// .env file
// DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"

프리즈마 모델 생성 및 DB Conneciton 생성

 

 

prisma 데이터 생성 (INSERT)

const prisma = new PrismaClient()

/** 단건 INSERT 예제1 **/
async function main() {
  const user = await prisma.user.create({
    data: {
      name: 'Alice',
      email: 'alice@prisma.io',
    },
  })
  console.log(user)
}

/** 단건 INSERT 예제2 **/
async function main() {
  const user = await prisma.user.create({
    data: {
      name: 'Bob',
      email: 'bob@prisma.io',
      posts: {
        create: {
          title: 'Hello World',
        },
      },
    },
  })
  console.log(user)
}

/** user, posts 테이블 데이터 INSERT **/
// 따로따로 데이터 INSERT할 수 있지만 연관관계가 있기 때문에 데이터 무결성 해결
const result = await prisma.user.create({
  data: {
    email: 'saanvi@prisma.io',
    posts: {
      createMany: {
        data: [{ title: 'My first post' }, { title: 'My second post' }],
      },
    },
  },
  include: {
    posts: true,
  },
})

// connect의 경우 이미 있는 데이터의 연관관계를 체크한 후에 있으면 데이터를 생성
const result = await prisma.user.create({
  data: {
    email: 'vlad@prisma.io',
    posts: {
      connect: [{ id: 8 }, { id: 9 }, { id: 10 }],
    },
  },
  include: {
    posts: true, // Include all posts in the returned object
  },
})

 

 

prisma 조회 (SELECT)

const prisma = new PrismaClient() // 프리즈마 객체 생성

/** user 테이블 전체 조회 **/
async function main() {
  const users = await prisma.user.findMany()
  console.log(users)
}

/** unique키 성질 조회**/
const getUser: User | null = await prisma.user.findUnique({
  where: {
    id: 22,
  },
})

/** 첫번째 결과값만 가져오기 **/
const user = await prisma.user.findFirst({
  include: {
    posts: true,
  },
})

/** 집계 조회 (갯수) **/
const relationCount = await prisma.user.findMany({
  include: {
    _count: {
      select: { posts: true },
    },
  },
})


/** 특정 필드 조회 + where절 **/
const getUser: object | null = await prisma.user.findUnique({
  where: {
    id: 22,
  },
  select: {
    email: true,
    name: true,
  },
})

/** 특정 필드 조회 Join **/
const users = await prisma.user.findMany({
  select: {
    name: true,
    posts: {
      select: {
        title: true,
      },
    },
  },
})

/** user 테이블 Join 조회 **/
async function main() {
  const usersWithPosts = await prisma.user.findMany({
    include: {
      posts: true,
    },
  })
  console.dir(usersWithPosts, { depth: null })
}

/** OR, NOT 절 **/
const result = await prisma.user.findMany({
  where: {
    OR: [
      {
        email: {
          endsWith: 'prisma.io',
        },
      },
      { email: { endsWith: 'gmail.com' } },
    ],
    NOT: {
      email: {
        endsWith: 'hotmail.com',
      },
    },
  },
  select: {
    email: true,
  },
})

/** 포함 여부 **/
const firstQueryResults = await prisma.post.findMany({
  take: 4,
  where: {
    title: {
      contains: 'Prisma' /* Optional filter */,
    },
  },
  orderBy: {
    id: 'asc',
  },
})


/** 정렬 **/
const usersWithPosts = await prisma.user.findMany({
  orderBy: [
    {
      role: 'desc',
    },
    {
      name: 'desc',
    },
  ],
  include: {
    posts: {
      orderBy: {
        title: 'desc',
      },
      select: {
        title: true,
      },
    },
  },
})

/** 커서기반 **/
// 처음 조회
const firstQueryResults = await prisma.post.findMany({
  take: 4,
  where: {
    title: {
      contains: 'Prisma' /* Optional filter */,
    },
  },
  orderBy: {
    id: 'asc',
  },
})
const lastPostInResults = firstQueryResults[3] // Remember: zero-based index! :)
const myCursor = lastPostInResults.id // Example: 29

// 다음 조회
const secondQueryResults = await prisma.post.findMany({
  take: 4,
  skip: 1, // Skip the cursor
  cursor: {
    id: myCursor,
  },
  where: {
    title: {
      contains: 'Prisma' /* Optional filter */,
    },
  },
  orderBy: {
    id: 'asc',
  },
})


/** OFFSET, LIMIT **/
const results = await prisma.post.findMany({
  skip: 3, // offset
  take: 4, // limit
})

 

prisma 조회 (UPDATE)

/** 특정 조건 연관관계 레코드 단건 업데이트 **/
const result = await prisma.user.update({
  where: {
    id: 6,
  },
  data: {
    posts: {
      update: {
        where: {
          id: 9,
        },
        data: {
          title: 'My updated title',
        },
      },
    },
  },
  include: {
    posts: true,
  },
})


/** 특정 조건 연관관계 레코드 다건 업데이트 **/
const result = await prisma.user.update({
  where: {
    id: 6,
  },
  data: {
    posts: {
      updateMany: {
        where: {
          published: true,
        },
        data: {
          published: false,
        },
      },
    },
  },
  include: {
    posts: true,
  },
})

 

prisma 조회 (DELETE)

/** 관련 데이터 전체 삭제**/
const result = await prisma.user.update({
  where: {
    id: 11,
  },
  data: {
    posts: {
      deleteMany: {},
    },
  },
  include: {
    posts: true,
  },
})

/** 관련 데이터 조건 삭제**/
const result = await prisma.user.update({
  where: {
    id: 11,
  },
  data: {
    posts: {
      deleteMany: {
        published: false,
      },
    },
  },
  include: {
    posts: true,
  },
})

 

prisma CLI

"prebuild": "rimraf dist && npx prisma generate" // 기존 컴파일 삭제 및 prisma 객체 생성 (model 변경시 필수 실행 명령어)
"db-pull": "npx prisma db pull", // db 변경사항을 schema.prisma에 업데이트
"db-push": "npx prisma db push", // schema.prisma 변경사항을 db에 적용
반응형
반응형

redux toolkit 설치

npm install @reduxjs/toolkit react-redux

 

 

store.ts

import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
})

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch

리덕스 객체 생성 및 슬라이스 등록

 

 

index.tsx

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import { store } from './app/store'
import { Provider } from 'react-redux'

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

서드파트에서 사용할 수 있게 Proivder 제공

 

 

counter/counter-slice.ts

import { createSlice } from '@reduxjs/toolkit'
import type { PayloadAction } from '@reduxjs/toolkit'

export interface CounterState {
  value: number
}

const initialState: CounterState = {
  value: 0,
}

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      // Redux Toolkit allows us to write "mutating" logic in reducers. It
      // doesn't actually mutate the state because it uses the Immer library,
      // which detects changes to a "draft state" and produces a brand new
      // immutable state based off those changes
      state.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    },
    incrementByAmount: (state, action: PayloadAction<number>) => {
      state.value += action.payload
    },
  },
})

// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions

export default counterSlice.reducer

전역값 저장을 위한 Reducer 생성

 

counter.tsx

import React from 'react'
import type { RootState } from '../../app/store'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'

export function Counter() {
  const count = useSelector((state: RootState) => state.counter.value)
  const dispatch = useAppDispatch();

  return (
    <div>
      <div>
        <button
          aria-label="Increment value"
          onClick={() => dispatch(increment())}
        >
          Increment
        </button>
        <span>{count}</span>
        <button
          aria-label="Decrement value"
          onClick={() => dispatch(counterSlice.actions.decrement())}
        >
          Decrement
        </button>
      </div>
    </div>
  )
}

useSelector를 통해 전역으로 관리되는 상태값 가져오기

dispatch를 통해 slice에 선언한 reducer 호출로 전역 상태값 저장하기

 

 

Redux Toolkit 사용 단계

  1. Redux Proivder
  2. Redux 객체 생성
  3. Slice 생성 (Reducer 전역값 저장)
  4. Slice Redux 객체에서 사용할 수 있게 등록
  5. tsx에서 useSelector나 Dispatch로 값을 가져오거나 전역값 저장
반응형