반응형
반응형

 

📝Apollo Server로 시작하기

Apollo Server 공식 문서와 제가 개인적으로 연습에 필요한 패키지로 진행했습니다

귀찮아서 ts가 아니라 js로 진행했다는 점 참고바랍니다

 

 

1. 프로젝트 폴더 만들기

mkdir graphql-server-example
cd graphql-server-example

 

2. Node 패키지 설치 및 셋팅

npm init --yes && npm pkg set type="module"

 

3. 필요 패키지 설치

npm install apollo-server graphql
npm install nodemon -D

 

4. 실행 파일 생성 (src/index.js)

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

// A schema is a collection of type definitions (hence "typeDefs")
// that together define the "shape" of queries that are executed against
// your data.

const books = [
    {
        title: 'The Awakening',
        author: 'Kate Chopin',
    },
    {
        title: 'City of Glass',
        author: 'Paul Auster',
    },
];

// Resolvers define how to fetch the types defined in your schema.
// This resolver retrieves books from the "books" array above.
const resolvers = {
    Query: {
        books: () => books,
    },
};

const typeDefs = `#graphql
# Comments in GraphQL strings (such as this one) start with the hash (#) symbol.

# This "Book" type defines the queryable fields for every book in our data source.
type Book {
    title: String
    author: String
}

# The "Query" type is special: it lists all of the available queries that
# clients can execute, along with the return type for each. In this
# case, the "books" query returns an array of zero or more Books (defined above).
type Query {
    books: [Book]
}
`;

// The ApolloServer constructor requires two parameters: your schema
// definition and your set of resolvers.
const server = new ApolloServer({
    typeDefs,
    resolvers,
});

// Passing an ApolloServer instance to the `startStandaloneServer` function:
//  1. creates an Express app
//  2. installs your ApolloServer instance as middleware
//  3. prepares your app to handle incoming requests
const { url } = await startStandaloneServer(server, {
    listen: { port: 4000 },
});

console.log(`🚀  Server ready at: ${url}`);

 

5. 실행 스크립트 작성 (package.json)

{
  "dependencies": {
    "@apollo/server": "^4.10.2",
    "apollo-server": "^3.13.0",
    "graphql": "^16.8.1"
  },
  "devDependencies": {
    "nodemon": "^3.1.0"
  },
  "name": "graphql-server-example",
  "version": "1.0.0",
  "main": "src/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon src/index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "type": "module"
}

 

6. npm start로 시작

localhost:4000/grpahql로 apollo server 접속

 

 

https://www.apollographql.com/docs/apollo-server/getting-started/

 

Get started with Apollo Server

This tutorial helps you: Obtain a basic understanding of GraphQL principlesDefine a GraphQL schema that represents the structure of your data setRun an instance of Apollo Server that lets you execute queries against your schema This tutorial assumes that y

www.apollographql.com

 

반응형
반응형

📝GET

{
  me {
    name
  }
}

// 위와 등치
http://myapi/graphql?query={me{name}}

HTTP GET 요청을 수신할 때 GraphQL 쿼리는 "query" 쿼리 문자열에 지정되어야 합니다

예를 들어, 다음 GraphQL 쿼리를 실행하려는 경우는 위와 같습니다

 

📝POST

/** ─── 호출 ───**/
{
  "query": "...",
  "operationName": "...",
  "variables": { "myVariable": "someValue", ... }
}

/** ─── 응답 ───**/
{
  "data": { ... },
  "errors": [ ... ]
}

표준 GraphQL POST 요청은 application/json콘텐츠 유형을 사용해야 하며 다음 형식의 JSON 인코딩 본문을 포함해야합니다

 

operationName및 variables선택적 필드입니다 그리고 operationName쿼리에 여러 작업이 있는 경우에만 필요합니다

응답의경우 잘못된 요청인 경우 errors 필드가 존재하며 그렇지 않을 땐 data 필드만 존재합니다

 

 

반응형
반응형

 

 

 

📝Json gzip 압축

GraphQL에서는 Accept-Encoding: gzip으로 Json 압축 기능도 제공한다

https://graphql.org/learn/best-practices/#json-with-gzip

 

GraphQL Best Practices | GraphQL

Copyright © 2024 The GraphQL Foundation. All rights reserved. For web site terms of use, trademark policy and general project policies please see https://lfprojects.org

graphql.org

 

반응형
반응형

📝Mutation

/** ─── 호출 ───**/
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}

/** ─── 호출 변수 ───**/
{
  "ep": "JEDI",
  "review": {
    "stars": 5,
    "commentary": "This is a great movie!"
  }
}

/** ─── 응답 ───**/
{
  "data": {
    "createReview": {
      "stars": 5,
      "commentary": "This is a great movie!"
    }
  }
}

Mutation의 경우는 데이터의 조회를 제외한 모든 것에 사용됩니다 특히 데이터 생성, 삭제, 수정에 해당하죠

 

쿼리 필드는 병렬로 실행되는 반면 변형 필드는 순차적으로 실행됩니다

한 요청에 두 개의 Mutation을 보내면 두번째 Mutation이 시작되기 전에 첫 번째 Mutation이 완료되어 우리 자신과의 경쟁 조건이 발생하지 않도록 보장합니다

 

쿼리와 마찬가지로 변형 필드가 객체 유형을 반환하는 경우 중첩 필드를 요청할 수 있습니다

이는 업데이트 후 객체의 새 상태를 가져오는 데 유용합니다

 

📝Query

HTTP에서 Get 방식 통신에 해당하는 걸로 데이터를 조회할 때 사용한다

 

기본 예제 1

/** ─── 호출 ───**/
Query Hero {
  hero {
    name
  }
}

// 생략
{
  hero {
    name
  }
}


/** ─── 응답 ───**/
{
  "data": {
    "hero": {
      "name": "R2-D2"
    }
  }
}

단일 작업 및 복잡하지 않는 단일 요청일 때 query 키워드랑 쿼리 이름을 생략할 수 있지만 생략해서 혼돈만 초래할 뿐 그냥 적어주자

 

기본 예제 2

/** ─── 호출 ───**/
Query Hero {
  hero {
    name
    # Queries can have comments!
    friends {
      name
    }
  }
}


/** ─── 응답 ───**/
{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        }
      ]
    }
  }
}

 

📝Query 파라미터

파라미터 사용 예제 1

# Human 타입 정의
type Human {
  id: ID!
  name: String!
  height: Float
}

# 쿼리 타입 정의
type Query {
  human(id: ID!): Human
}

/** ─── 호출 ───**/
{
  human(id: "1000") {
    name
    height
  }
}

/** ─── 응답 ───**/
{
  "data": {
    "human": {
      "name": "Luke Skywalker",
      "height": 1.72
    }
  }
}

 

 

정의한 쿼리 타입 토대로 파라미터를 넣어서 요청할 수 있다

 

파라미터 사용 예제 2

enum Unit {
  METER
  FOOT
}

# Human 타입 정의
type Human {
  id: ID!
  name: String!
  height(unit: Unit = METER): Float
}

/** ─── 호출 ───**/
{
  human(id: "1000") {
    name
    height(unit: FOOT)
  }
}

/** ─── 응답 ───**/
{
  "data": {
    "human": {
      "name": "Luke Skywalker",
      "height": 5.6430448
    }
  }
}

 

📝Query 별칭

별칭 예제

/** ─── 호출 ───**/
{
  empireHero: hero(episode: EMPIRE) {
    name
  }
  jediHero: hero(episode: JEDI) {
    name
  }
}

/** ─── 응답 ───**/
{
  "data": {
    "empireHero": {
      "name": "Luke Skywalker"
    },
    "jediHero": {
      "name": "R2-D2"
    }
  }
}

두 필드는 hero로 충돌이 될 예정이였지만 alias로 인해 문제 없이 진행되게 했다

 

📝Query Fragment

fragment 예제

fragment comparisonFields on Character {
  name
  appearsIn
  friends {
    name
  }
}


/** ─── 호출 ───**/
{
  leftComparison: hero(episode: EMPIRE) {
    ...comparisonFields
  }
  rightComparison: hero(episode: JEDI) {
    ...comparisonFields
  }
}

/** ─── 응답 ───**/
{
  "data": {
    "leftComparison": {
      "name": "Luke Skywalker",
      "appearsIn": [
        "NEWHOPE",
        "EMPIRE",
        "JEDI"
      ],
      "friends": [
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        },
        {
          "name": "C-3PO"
        },
        {
          "name": "R2-D2"
        }
      ]
    },
    "rightComparison": {
      "name": "R2-D2",
      "appearsIn": [
        "NEWHOPE",
        "EMPIRE",
        "JEDI"
      ],
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        }
      ]
    }
  }
}

fragment를 이용해 재사용할 수 있게 하고 반복적인 코드를 줄일 수 있습니다
on Type명으로 꼭 타입을 지정해줘야합니다 여기에서는 Character라는 타입입니다

 

📝Query 동적 파라미터

동적 파라미터 예제 1

/** ─── 호출 ───**/
query HeroNameAndFriends($episode: Episode) {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}

/** ─── 호출 변수 ───**/
{
  "episode": "JEDI"
}

/** ─── 응답 ───**/
{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        },
        {
          "name": "Leia Organa"
        }
      ]
    }
  }
}

변수를 이용해 동적으로 데이터를 받을 수 있다

 

동적 파라미터 예제 2

query HeroNameAndFriends($episode: Episode = JEDI) {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}

기본값 설정도 가능함

 

📝지시문

/** ─── 호출 ───**/
query Hero($episode: Episode, $withFriends: Boolean!) {
  hero(episode: $episode) {
    name
    friends @include(if: $withFriends) {
      name
    }
  }
}

/** ─── 호출 변수 ───**/
{
  "episode": "JEDI",
  "withFriends": false
}

/** ─── 응답─**/
{
  "data": {
    "hero": {
      "name": "R2-D2"
    }
  }
}

지시문으로 필드를 동적으로 받을 수 있다

  • @include(if: Boolean)
    • 인수가 다음인 경우에만 결과에 이 필드를 포함합니다
  • @skip(if: Boolean)
    • 인수가 이면 이 필드를 건너뛰십시오

 

 

반응형
반응형

📝Exclude<UnionType, ExcludedMembers>

type T0 = Exclude<"a" | "b" | "c", "a">;
// type T0 = "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">;
// type T1 = "c"
type T2 = Exclude<string | number | (() => void), Function>;
// type T2 = string | number

UnionType에 할당된 것에 제외할 멤버를 제외하고 Type 형식을 구성합니다

 

📝Extract<Type, Union>

type T0 = Extract<"a" | "b" | "c", "a" | "f">;
// type T0 = "a"
type T1 = Extract<string | number | (() => void), Function>;
// type T1 = () => void

Type과 Union의 교집합에 해당하는 부분으로 Type형식을 구성합니다

 

📝NonNullable<Type>

type T0 = NonNullable<string | number | undefined>;
// type T0 = string | number
type T1 = NonNullable<string[] | null | undefined>;
// type T1 = string[]

null및 undefined를 제외하여 유형을 구성합니다

 

📝Parameters<Type>

function greet(name: string, age: number): string {
  return `Hello, ${name}. You are ${age} years old.`;
}
type GreetParameters = Parameters<typeof greet>;
// GreetParameters 타입은 [string, number]와 동일합니다.
const user: GreetParameters = ["John Doe", 30];

유틸리티 타입은 함수의 매개변수 타입들을 튜플 타입으로 추출합니다

 

📝ConstructorParameters

class User {
  constructor(public name: string, public age: number) {}
}

// User 생성자의 매개변수 타입을 추출합니다.
type UserConstructorParameters = ConstructorParameters<typeof User>;

// UserConstructorParameters는 [string, number] 타입과 동일합니다.
const userParams: UserConstructorParameters = ["Jane Doe", 32];

클래스 생성자의 매개변수 타입들을 튜플로 추출합니다

 

📝ReturnType<Type>

function getUser() {
  return { name: "John Doe", age: 30 };
}

// getUser 함수의 반환 타입을 추출합니다.
type User = ReturnType<typeof getUser>;

// User 타입은 { name: string; age: number; }와 동일합니다.
const user: User = { name: "Jane Doe", age: 32 };

함수의 반환 타입을 추출하는 유틸리티 타입입니다

반응형
반응형

📝Awaited<Type>

type A = Awaited<Promise<string>>;
// type A = string
type B = Awaited<Promise<Promise<number>>>;
// type B = number
type C = Awaited<boolean | Promise<number>>;
// type C = number | boolean

프로미스(Promise)가 resolve된 후의 타입을 나타내는 유틸리티 타입입니다

📝Partial<Type>

interface Todo {
  title: string;
  description: string;
}
 
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
  return { ...todo, ...fieldsToUpdate };
}
 
const todo1 = {
  title: "organize desk",
  description: "clear clutter",
};
 
const todo2 = updateTodo(todo1, {
  description: "throw out trash",
});

기존 타입의 구조를 유지하면서 모든 필드를 필수가 아닌 선택적으로 변경할 수 있습니다

 

📝Required<Type>

interface Props {
  a?: number;
  b?: string;
}
 
const obj: Props = { a: 5 };
const obj2: Required<Props> = { a: 5 };
// Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.

해당 필드에 내용이 반드시 필요하다는 선언이 가능합니다

 

📝Readonly<Type>

interface Todo {
  title: string;
}
 
const todo: Readonly<Todo> = {
  title: "Delete inactive users",
};
 
todo.title = "Hello";
Cannot assign to 'title' because it is a read-only property.

생성된 유형의 속성을 재할당할 수 없습니다

 

📝Record<Keys, Type>

Record <K, V>

export enum categoryEnum {
  FC12000000 = 'FC12000000',
  FC14000000 = 'FC14000000',
}

static readonly names: Record<categoryEnum, string> = {
    FC12000000: '취미 / 문구 / 도서',
    FC14000000: 'e쿠폰 / 여행 / 렌탈',
}

Typescript에서는 Record라는게 존재하는데 이것은 Map과 같이 Key, Value로 사용 가능한 타입이다

 

📝Pick<Type, Keys>

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}
 
type TodoPreview = Pick<Todo, "title" | "completed">;
 
const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};

속성 집합(문자열 리터럴 또는 문자열 리터럴의 합집합) 을 선택하여 유형을 구성합니다

 

📝Omit<Type, Keys>

interface Todo {
  title: string;
  description: string;
  completed: boolean;
  createdAt: number;
}
 
type TodoPreview = Omit<Todo, "description">;
 
const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
  createdAt: 1615544252770,
};

(문자열 리터럴 또는 문자열 리터럴의 합집합) 에서 모든 속성을 선택한 Type다음 제거 하여 유형을 구성합니다

반응형
반응형

📝CommonJS

JavaScript를 브라우저 밖에서 즉, 서버에서 실행할 수 있게 하는 모듈화 표준입니다 → 간단히 JavaScript 문법이라 생각하세요

현재는 ECMAScript module에 사람들이 익숙하고 많이 쓰지만 태초에는 CommonJS 방식만 존재했습니다

 

const math = require('./math.js')
console.log(math.sum(1, 2))

 

 

지금은 import를 사용하지만 require로 외부 모듈을 불러오는 형식입니다

 

 

📝ECMAScript

자바스크립트 프로그래밍 언어의 표준화된 명세로 자바스크립트의 표준은 거의 여기에 맞춰서 개발되고 있습니다

처음에는 규범이라는 게 없고 Explorer 독점이였다가 Chrome이 나오면서 위협을 느끼고 웹브라우저를 가지고 있는 회사들은 열심히 개발했는데 서로 규범이 다르다보니 개발자는 여러개를 개발해야하는 거지같은 상황이 만들어졌습니다

그래서 다같이 모여서 규범을 정한 게 ECMAScript라고 생각하시면 됩니다

 

📝Flux패턴

웹 애플리케이션을 위해 Facebook에 의해 개발된 아키텍처 패턴입니다

React도 이걸로 처음에 만들어졌다가 현재는 Hook을 기반으로한 아키텍처를 채택했습니다

이 패턴을 기반으로 만들어진 Redux가 있고 Redux는 이걸 개선시켜서 현재는 사용되지 않는 패턴이라고 생각합니다

 

  • Dispatcher
    • 모든 데이터 흐름을 관리하는 중앙 허브입니다
  • Store
    • 애플리케이션 상태를 보유하고 로직을 관리하는 곳입니다 디스패처로부터 액션을 받아 상태를 업데이트하고 변경사항이 있을 때 뷰에 알립니다
  • Actions
    • 뷰에서 사용자 상호작용에 응답하여 발생한 간단한 객체로 타입 속성을 포함합니다
  • Views
    • React 컴포넌트 같은 사용자 인터페이스 요소로 스토어 변화 감지해 새로 고칩니다
반응형
반응형

📝인덱스 (index)

interface Person {
  name: string;
  age: number;
}

type NameType = Person["name"]; // string
type AgeType = Person["age"]; // number

const age: AgeType = 50
const nm: NameType = "Emily"

index라는 걸 이용해 해당 interface의 필요 타입을 발췌할 수 있다

 

 

📝조건부 유형

/** 기본 형태 **/
type IsStringType<T> = T extends string ? string[] : number[];

type T1 = IsStringType<string>; // type T1 = string[]
type T2 = IsStringType<number>; // type T2 = number[]

const a: T1 = ['홍길동', '임꺾정', '박혁거세'];
const b: T2 = [1000, 2000, 3000];

/** 유니온 타입인 경우 **/
type ToArray<Type> = Type extends any ? Type[] : never;
type StrArrOrNumArr = ToArray<string | number>;
// type StrArrOrNumArr = string[] | number[]

기본형태의 경우 제너릭 T가 string을 상속받을 수 있다면 string[]이 선언되고 그렇지 않으면 number[]가 선언된다

유니온의 타입의 경우 유니온 타입으로 생성된다

 

 

📝템플릿 리터럴형

type World = "world";

type Greeting = `hello ${World}`;
// type Greeting = "hello world"

type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";
 
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
// type AllLocaleIDs = "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"

 

자바스크립트 이벤트 바인딩 on 재선언해서 타입스크립트 적용시키는 활용 예제

type PropEventSource<Type> = {
    on(eventName: `${string & keyof Type}Changed`, callback: (newValue: any) => void): void;
};
 
/// Create a "watched object" with an `on` method
/// so that you can watch for changes to properties.
declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>;

 

📝리터럴 타입 조작 유형

type Greeting = "Hello, world"
type ShoutyGreeting = Uppercase<Greeting>
// type ShoutyGreeting = "HELLO, WORLD"

type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`
type MainID = ASCIICacheKey<"my_app">
// type MainID = "ID-MY_APP"

리터럴 타입을 조작할 수 있고 다양한 옵션들이 존재한다

 

 

📝Flatten

const a = {
  a : "key 'a'",
  b : {
    aa : "key 'b.aa'",
    bb : "key 'b.bb'",
  },
  c : {
    aa : {
      aaa : "key 'c.aa.aaa'",
      bbb : "key 'c.aa.bbb'",
    }
  },
}


const a = {
  "a" : "key 'a'",
  "b.aa" : "key 'b.aa'",
  "b.bb" : "key 'b.bb'",
  "c.aa.aaa" : "key 'c.aa.aaa'"
  "c.aa.bbb" : "key 'c.aa.bbb'"
}

Flatten이란 깊이가 1 이상인 object들을 일정한 키 생성 규칙에 따라 깊이가 1로 고정된 오브젝트로 전환하는 기능을 말한다

 

 

🔗 참고 및 출처

 

https://velog.io/@egoavara/flatten-%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8-%ED%83%80%EC%9E%85-%EC%B6%94%EB%A1%A0

https://inpa.tistory.com/entry/TS-%F0%9F%93%98-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%A1%B0%EA%B1%B4%EB%B6%80-%ED%83%80%EC%9E%85-%EC%99%84%EB%B2%BD-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0

반응형
반응형

📝JPEG

  • 1986년 국제표준화기구(ISO)에서 출시 했다
  • 1,680만 개의 색상 표현이 가능하다
  • 사람 눈으로 볼 수 없는 시각적 데이터를 제거하고 색상 변형을 평균화 시킨 손실 압축의 기술 이용
  • 전문 사진작가는 후처리 또는 인쇄를 위해 이미지의 세부 정보를 유지하기를 원하기 때문에 손실 압축 방식을 사용하는 JPEG 촬영을 꺼리는 경향
  • 여담으로 옛날에는 OS에 따라 확장자 길이 제한이 있어 JPEG를 JPG라고 표현했다

 

📝GIF

  • 1987년에 출시 되었다
  • 256가지의 색상을 지원하며 애니메이션을 지원하기 때문에 밈에 많이 사용된다
  • 무손실 압축 파일로 이미지 품질이 저하되지 않지만 색상 제한이 있어서 해상도가 낮거나 흐려질 수 있습니다

 

📝PNG

  • 1995년에 IT 전문가 Oliver Fromme에 의해 PING이라는 이름으로 출시 되었다
  • GIF 포맷의 차세대 버전으로 출시되었으며 1,600만 개의 색상 지원
  • GIF와 달리 애니메이션을 지원하지 않고 손실 압축을 하지 않고 그냥 압축 방식을 사용하기 때문에 데이터 크기는 JPEG보다 크다
  • 손실이 없고 투명 배경을 지원하기 때문에 디자이너들이 자주 애용

 

📝BMP

  • 마이크로소프트에서 1990년대에 윈도우 운영 체제용 BMP 포맷을 개발했다
  • 압축이 되지 않아 다른 확장자에 비해 용량은 큰 반면2D사진, 아이콘, 화면 캡처의 경우 BMP가 더 유용하다

 

📝SVG

  • 1990년대 후반에 W3C에서 새로운 유형 벡터 그래픽 포맷을 개발했다
  • 옛날에 개발되었던 반면 인기를 얻는데 까지는 시간이 걸렸다 (2017년까지 사용이 적었음)
  • XML코드로 작성하기 때문에 정보를 텍스트로 가지고 있어 검색엔진 최적화에 도움이 된다
  • 로고, 일러스트레이션 따위에 사용하기 좋고 고품질 이미지에는 픽셀이 부족해 표현하기 어렵다

 

📝WebP

  • Google에서 2010년에 개발한 파일 포맷으로 고품질 이미지를 표현할 수 있으며 JPEG, PNG보다 파일 크기가 더 작아진다
  • 손실압축 / 무손실압축을 지원한다 Google에서는 무손실로 PNG보다 최대 26%까지 줄어들 수 있다고 주장
  • gif 형식 애니메이션도 지원한다
  • 호환성 확인 → https://caniuse.com/webp

 

📝Avif

  • Webp의 후계자격이며 2019년에 1.0버전이 나왔기 때문에 최근 이미지 포맷 기술이다
  • AV1 기반(비디오 인코딩)으로 돌아가고 AV1은 구글, 마이크로소프트, 애플 등에서 같이 만든 비디오 코덱으로 문제가 없을 가능성이 높다
  • 단순한 이미지 형식의 경우 무손실 Webp가 더 좋지만 고품질의 경우 Avif가 성능면에서 좋다
  • 호환성 확인 → https://caniuse.com/avif
반응형