반응형
반응형
import {
  GraphQLDecimal,
  transformToDecimal,
} from 'prisma-graphql-type-decimal';
import { Decimal } from '@prisma/client/runtime/library';
import { Transform, Type } from 'class-transformer';

@InputType()
export class Item {
  @Field(() => GraphQLDecimal, {
    description: '기본입찰가',
    nullable: true,
  })
  @Transform(transformToDecimal)
  @Type(() => Object)
  basic_bid_price?: Decimal;
  ...
}

Transform 변형 및 해당 Type을 Object로 설정해야 에러가 발생 안 하고 인식한다

 

@InputType()
export class Test {
  @Field(() => GraphQLDecimal, { nullable: true })
  @Transform(transformToDecimal)
  @Type(() => Object)
  smart_bid_price: Decimal;
}

@InputType()
export class TestInput {
  @Type(() => Test) // 배열 내 객체들을 Test 타입으로 변환
  @Field(() => [Test])
  test: Test[];
}

Object를 이용한 배열일 때는 이런식으로 사용하면 된다

반응형
반응형
  • 파일
    • ad-item
  • 디렉토리
    • ad-item
  • 변수명
    • camelCase → rightBorderCell
  • model
    • ad-item.model
  • module
    • ad-item.module
  • resolver
    • ad-item.resolver
  • service
    • ad-item.service
  • dto
    • ad-item.input
  • controller
    • ad-item.controller
  • class명
    • AdItemController, AdItemResolver, AdItemInput, AdItemModule,AdItemService
  • enum
반응형
반응형

 

https://www.npmjs.com/package/graphql-upload

 

graphql-upload

Middleware and an Upload scalar to add support for GraphQL multipart requests (file uploads via queries and mutations) to various Node.js GraphQL servers.. Latest version: 16.0.2, last published: a year ago. Start using graphql-upload in your project by ru

www.npmjs.com

"graphql-upload": "^13.0.0"
"@types/graphql-upload": "^8.0.12"

13버전으로 패키지 설치 → 14버전 이상일 경우 패키지가 .mjs로 만들어져있어서 ts.config설정을 따로 하지 않는 이상 경로 에러 발생

 

app.use(graphqlUploadExpress());

 

Nest.js에 해당 설정 추가해야 파일 업로드 (Multipart/form-data 및 파일 스트림 전송 가능)

 

import { InputType, Field } from '@nestjs/graphql';
import { FileUpload, GraphQLUpload } from 'graphql-upload';

@InputType()
export class FileInput {
  @Field(() => String, { description: '첨부 파일명' })
  file_name: string;

  @Field(() => String, { description: '첨부 파일 타입' })
  file_content_type: string;

  @Field(() => GraphQLUpload, { description: '첨부 파일 blob' })
  file_data: Promise<FileUpload>;
}

GraphQL Mapping Input

 

const data = await fileInput.file_data;
const mimetype = data.mimetype;
const fileName = data.filename;
const fileStream = data.createReadStream();

const chunks = [];
for await (const chunk of fileStream) {
  chunks.push(chunk);
}
const fileBuffer = Buffer.concat(chunks);

비즈니스 로직

 

const [formData, setFormData] = useState(new FormData());

const changeFile = (event: React.ChangeEvent<HTMLInputElement>) => {
    if (event.target.files && event.target.files.length > 0) {
      const fileFormData = new FormData();
      const file = event.target.files?.[0];

      /** GraphQL 쿼리와 변수를 formData에 추가 **/
      fileFormData.append(
        'operations',
        JSON.stringify({
          query: `
            mutation fileUpload($fileInput: FileInput!) {
              fileUpload(fileInput: $fileInput)
            }
          `,
          variables: {
            fileInput: {
              file_name: file.name,
              file_content_type: file.type,
            },
          },
        }),
      );

      /** 이진 데이터 매핑 **/
      fileFormData.append(
        'map',
        JSON.stringify({ '0': ['variables.fileInput.file_data'] }),
      );

      /** 이진 데이터 매핑 **/
      fileFormData.append('0', file);
      setFormData(fileFormData);
    }
  };

  const upload = useCallback(() => {
    fetch('http://localhost:4000/graphql', {
      method: 'POST',
      headers: {
        'x-apollo-operation-name': 'UploadFile', // application/json외 CSRF 방지 허용 Header
      },
      body: formData,
    })
      .then((response) => response.json())
      .then((data) => console.log(data))
      .catch((error) => console.error(error));
  }, [formData]);
  
<input type="file" onChange={changeFile} />
<button onClick={() => upload()}>Upload</button>

클라이언트에서 호출할 때 mutlipart/form-data 형식으로 보내야한다

이진 데이터의 경우 따로 매핑이 필요하다 → GraphQL의 경우 applicaiton/json으로만 통신하게 되어있기 때문에 나머지는 막아놨다 허용하게 하려면 Header에 값을 붙여서 보내야 mutlipart/form-data도 허용이 된다

 

자세한 내용들은 아래 참고 및 출처를 확인 하길 바랍니다

 

!!!

근데 해보니까 이진데이터를 보내서 받아 DB에 저장까지는 되었다만 DB 이진 데이터를 다시 GraphQL로 return이 안 된다 GraphQL은 application/json 타입으로만 return하는 형태로 만들어져있기 때문에 Response 객체를 얻어서 조작하는 행위가 안 됨 → Response에 Buffer를 담고 어떤 파일 형식인지 Header에 보내면 호출 시 해당 파일 보내질텐데 그게 안 됨 이럴바에 그냥 base64로 보내고 base64로 받자

 

🔗 참고 및 출처

https://medium.com/prisma-korea/graphql-%ED%8C%8C%EC%9D%BC-%EC%A0%84%EC%86%A1-b41c5f4deca0

https://graphql-compose.github.io/docs/guide/file-uploads.html

https://www.apollographql.com/docs/apollo-server/v3/data/file-uploads/

 

반응형