Skip to content

Conversation

@asjasj3964
Copy link
Collaborator

Sweepic Server PR List

close #183

⚒️develop의 최신 커밋을 pull 받았나요?

  • 최신 커밋 업데이트

🔍️ 이 PR을 통해 해결하려는 문제가 무엇인가요?

어떤 기능을 구현한건지, 이슈 대응이라면 어떤 이슈인지 PR이 열리게 된 계기와 목적을 Reviewer 들이 쉽게 이해할 수 있도록 적어 주세요
일감 백로그 링크나 다이어그램, 피그마를 첨부해도 좋아요

  • 메모 텍스트 저장 API(@jonaeunnn 파트) tsoa 적용
  • registerRoutes로 라우팅 등록 시 multer 옵션을 추가하지 않고 여러 multer 객체(텍스트로 저장 시 & 사진으로 저장)를 사용할 수 있도록 구현

✨ 이 PR에서 핵심적으로 변경된 사항은 무엇일까요? (핵심 작업 내용)

문제를 해결하면서 주요하게 변경된 사항들을 적어 주세요

  • 메모 텍스트 저장 API(Post, Patch) 수행 시 스웨거 상에서 이미지를 업로드 할 수 있고, formdata로만 사진을 보낼 수 있도록 수정하였습니다.
    ...
    @Route('memo')
    export class MemoCreateUpdateOCRController extends Controller {
      /**
       * 새로운 폴더를 생성하고, 이미지에서 OCR 텍스트를 추출하여 이미지와 텍스트를 저장하는 API입니다.
       *
       * @summary 폴더 생성 및 OCR 수행
       * @param req
       * @param folderName 생성할 폴더의 이름
       * @returns 성공 시 폴더를 생성하고 텍스트를 저장한 결과를 반환합니다.
       */
      @Post('/text-format/folders')
      @Tags('memo-ai')
      @Response<ITsoaErrorResponse>(StatusCodes.BAD_REQUEST, '잘못된 요청 데이터', {
        resultType: 'FAIL',
        success: null,
        error: {
          errorCode: 'SRH-400',
          reason: '입력 데이터가 유효하지 않습니다.',
          data: {reason: 'base64_image, user_id, folder_name이 필요합니다.'},
        },
      })
      @Response<ITsoaErrorResponse>(StatusCodes.BAD_REQUEST, '잘못된 요청 데이터', {
        resultType: 'FAIL',
        success: null,
        error: {
          errorCode: 'PHO-400',
          reason: '사진 데이터가 유효하지 않습니다.',
          data: {reason: '올바른 Base64 이미지 형식이 아닙니다.'},
        },
      })
      @Response<ITsoaErrorResponse>(StatusCodes.BAD_REQUEST, '잘못된 요청 데이터', {
        resultType: 'FAIL',
        success: null,
        error: {
          errorCode: 'PHO-400',
          reason: '사진 데이터가 유효하지 않습니다.',
          data: {reason: '텍스트를 추출할 사진이 없습니다.'},
        },
      })
      @Response<ITsoaErrorResponse>(StatusCodes.BAD_REQUEST, '잘못된 요청 데이터', {
        resultType: 'FAIL',
        success: null,
        error: {
          errorCode: 'PHO-400',
          reason: '사진 데이터가 유효하지 않습니다.',
          data: {reason: '이미지에서 텍스트를 찾지 못하였습니다.'},
        },
      })
      @Response<ITsoaErrorResponse>(
        StatusCodes.NOT_FOUND,
        '존재하지 않은 데이터 조회 에러',
        {
          resultType: 'FAIL',
          success: null,
          error: {
            errorCode: 'FOL-404',
            reason: '해당 폴더를 찾을 수 없습니다.',
            data: {folderId: '1'},
          },
        },
      )
      @Response<ITsoaErrorResponse>(StatusCodes.CONFLICT, '중복 데이터 에러', {
        resultType: 'FAIL',
        success: null,
        error: {
          errorCode: 'FOL-409',
          reason: '이미 존재하는 폴더 이름입니다.',
          data: {folderName: 'string'},
        },
      })
      @SuccessResponse(StatusCodes.OK, '폴더 생성 및 텍스트 변환')
      @Example({
        resultType: 'SUCCESS',
        error: null,
        success: {
          folder_id: '1',
          image_text: '이번 수업 시간은 사회 과학 시간이다.',
        },
      })
      public async createFolderOCR(
        @Request() req: ExpressRequest,
        @FormField('folder_name') folderName: string,
      ): Promise<ITsoaSuccessResponse<{folder_id: string; image_text: string}>> {
        try {
          if (!req.file) {
            throw new PhotoDataNotFoundError({
              reason: '텍스트를 추출할 사진이 없습니다.',
            });
          }
    
          //파일 업로드가 있는 경우 base64 변환
          const base64_image = `data:image/png;base64,${req.file.buffer.toString('base64')}`;
    
          const user_id = BigInt(req.user!.id);
          const folder_name = folderName;
    
          // 유효성 검사-데이터가 하나라도 빠지지 않도록
          // 유효성 검사
          if (!base64_image || !user_id || !folder_name) {
            throw new DataValidationError({
              reason: 'base64_image, user_id, folder_name이 필요합니다.',
            });
          }
          //base64 이미지 검증 - 올바른 형태인지
          if (!isValidBase64(base64_image)) {
            throw new PhotoValidationError({
              reason: '올바른 Base64 이미지 형식이 아닙니다.',
            });
          }
    
          // 서비스 호출
          const result = await processOCRAndSave({
            base64_image,
            user_id,
            folder_name,
          });
    
          // 성공 응답
          return new TsoaSuccessResponse({
            folder_id: result.folder_id,
            image_text: result.image_text,
          });
        } catch (error) {
          throw error;
        }
      }
    }
    ...
    
  • 스위픽은 메모 사진 저장 API, 메모 텍스트 저장 API와 같이 파일을 업로드 하는 API가 있습니다. 그래서 상황에 맞게 파일 업로드를 처리하는 multer 미들웨어를 사용해야 합니다. 스위픽이 사용하는 multer 미들웨어는 2가지가 있습니다.
    1. 메모 사진을 업로드해 S3에 저장하는 imageUploader 미들웨어
    2. 메모 사진을 업로드해 텍스트만 추출하고 저장하지 않는 upload 미들웨어
  • 이전에는 RegisterRoutes(app, {multer: imageUploader}) 이런 식으로 registerRoutes로 라우팅 등록 시 multer 옵션을 추가하여 @uploadedfile() 데코레이터로 파일을 업로드할 때 자체적인 multer 미들웨어가 아닌 커스텀 multer 미들웨어(imageUploader)를 이용하도록 구현해주었습니다. 그러나 이는 메모 텍스트 저장 API를 수행할 때에도 imageUploader 미들웨어를 거치게 되어 해당 사진을 불필요하게 S3에 저장하게 된다는 문제가 있습니다.
  • 때문에 1개 이상의 커스텀 multer 미들웨어를 적절한 상황에 쓸 수 있는 방법을 필요했습니다. 그래서 @uploadedfile() 데코레이터를 사용하지 않고 tson.json의 spec에 파일을 업로드하는 requestBody 설정을 추가해 기존의 spec과 병합되도록 하는 방법을 채택하였습니다(이 과정에서 링크가 많은 도움을 주셨습니다!).
    // config.tsoa.json
    
    {
      "noImplicitAdditionalProperties": "throw-on-extras",
      "entryFile": "src/app.ts",
      "controllerPathGlobs": ["src/controllers/*.controller.ts"],
      "spec": {
        ...
        "spec": {
          "servers": [
            {
              "url": "http://localhost:3000",
              "description": "Sweepic local server"
            },
            {
              "url": "http://3.37.137.212:3000",
              "description": "Sweepic server"
            }
          ],
          "paths": {
            "/memo/image-format/folders": {
              "post": {
                "requestBody": {
                  "required": true,
                  "content": {
                    "multipart/form-data": {
                      "schema": {
                        "type": "object",
                        "properties": {
                          "image": {
                            "type": "string",
                            "format": "binary",
                            "description": "파일 업로드"
                          }
                        }
                      }
                    }
                  }
                }
              }
            },
            "/memo/image-format/folders/{folderId}": {
              "post": {
                "requestBody": {
                  "required": true,
                  "content": {
                    "multipart/form-data": {
                      "schema": {
                        "type": "object",
                        "properties": {
                          "image": {
                            "type": "string",
                            "format": "binary",
                            "description": "파일 업로드"
                          }
                        }
                      }
                    }
                  }
                }
              }
            },
            "/memo/text-format/folders/{folderId}": {
              "patch": {
                "requestBody": {
                  "required": true,
                  "content": {
                    "multipart/form-data": {
                      "schema": {
                        "type": "object",
                        "properties": {
                          "base64_image": {
                            "type": "string",
                            "format": "binary",
                            "description": "OCR 처리를 위한 이미지 업로드"
                          }
                        }
                      }
                    }
                  }
                }
              }
            },
            "/memo/text-format/folders": {
              "post": {
                "requestBody": {
                  "required": true,
                  "content": {
                    "multipart/form-data": {
                      "schema": {
                        "type": "object",
                        "properties": {
                          "base64_image": {
                            "type": "string",
                            "format": "binary",
                            "description": "OCR 처리를 위한 이미지 업로드"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        ...
      },
      "routes": {
        ...
      }
    }
    
    // src/app.ts
    
    ...
    app.post('/memo/text-format/folders', upload.single('base64_image'));
    app.patch('/memo/text-format/folders/:folderId', upload.single('base64_image'));
    app.post('/memo/image-format/folders', imageUploader.single('image'));
    app.post('/memo/image-format/folders/:folderId', imageUploader.single('image'));
    RegisterRoutes(app);
    ...
    
    • 그 결과 스웨거 상에서도 파일 업로드가 가능할 수 있게 되었고, 해당 엔드포인트들에 imageUploader(사진 S3 저장) multer와 upload(사진을 받아 텍스트 추출) multer를 미들웨어로 등록해 각 엔드포인트에서 multer를 적절히 사용할 수 있게 되었습니다.

🤚 동작 확인

기능을 실행했을 때 정상 동작하는지 여부를 확인하고 스크린 샷을 올려주세요

  • 폴더 업데이트 및 OCR 수행 API
    image
    image
  • 폴더 생성 및 OCR 수행 API
    image
    image
  • 위의 두 API 테스트 시 출력된 req.file
    image
    • upload 미들웨어를 거친 것을 확인할 수 있다.
  • 사진 저장 API
    image
    image
  • 폴더 생성 및 사진 저장 API
    image
    image
  • 위의 두 API 테스트 시 출력된 req.file
    image
    • imageUploader 미들웨어를 거친 것을 확인할 수 있다.

🔖 핵심 변경 사항 외에 추가적으로 변경된 부분이 있나요?

없으면 "없음" 이라고 기재해 주세요

  • tsoa 적용에 실패할 경우를 대비해서 tsoa를 적용한 파일과 그렇지 않은 파일을 구분했었는데 테스트에 이상이 없어서 tsoa를 적용하지 않은 파일들을 삭제하였습니다. 동시에 낭니 API 포함 docs/v1에 있던 API들이 모두 사라졌습니다. 해당 API들은 모두 docs/v2에서 확인하실 수 있습니다.

🙏 Reviewer 분들이 이런 부분을 신경써서 봐 주시면 좋겠어요

개발 과정에서 다른 분들의 의견은 어떠한지 궁금했거나 크로스 체크가 필요하다고 느껴진 코드가 있다면 남겨주세요

  • @jonaeunnn 개발 의도에 맞게 잘 tsoa 적용이 되었는지 확인 부탁드립니다!
  • 부족한 점이 있으면 알려주세요.

🩺 이 PR에서 테스트 혹은 검증이 필요한 부분이 있을까요?

테스트가 필요한 항목이나 테스트 코드가 추가되었다면 함께 적어주세요

  • 없음

📌 PR 진행 시 이러한 점들을 참고해 주세요

  • Reviewer 분들은 코드 리뷰 시 좋은 코드의 방향을 제시하되, 코드 수정을 강제하지 말아 주세요.
  • Reviewer 분들은 좋은 코드를 발견한 경우, 칭찬과 격려를 아끼지 말아 주세요.
  • Review는 특수한 케이스가 아니면 Reviewer로 지정된 시점 기준으로 2일 이내에 진행해 주세요.
  • Comment 작성 시 Prefix로 P1, P2, P3 를 적어 주시면 Assignee가 보다 명확하게 Comment에 대해 대응할 수 있어요
    • P1 : 꼭 반영해 주세요 (Request Changes) - 이슈가 발생하거나 취약점이 발견되는 케이스 등
    • P2 : 반영을 적극적으로 고려해 주시면 좋을 것 같아요 (Comment)
    • P3 : 이런 방법도 있을 것 같아요~ 등의 사소한 의견입니다 (Chore)


📝 Assignee를 위한 CheckList

  • To-Do Item

@jjiinaaa jjiinaaa merged commit d851ded into develop Feb 10, 2025
1 check passed
@jjiinaaa jjiinaaa deleted the refactor/SWEP-89 branch February 10, 2025 15:42
jjiinaaa added a commit that referenced this pull request Feb 10, 2025
* [SWEP-58] 스웨거 문서 작성 (#187)

* [SWEP-58] 스웨거 문서 작성

* [SWEP-58] 사진삭제 스웨거 주석 작성

* [SWEP-58] 스웨거 문서 변경

* [SWEP-89] 메모 텍스트 저장 API tsoa 적용 및 여러 multer 객체 사용할 수 있도록 구현 (#189) (#191)

Co-authored-by: JinHa Park <160022452+jjiinaaa@users.noreply.github.com>

---------

Co-authored-by: Socializedistp <159607145+Socializedistp@users.noreply.github.com>
Co-authored-by: asjasj3964 <84120715+asjasj3964@users.noreply.github.com>
jjiinaaa added a commit that referenced this pull request Feb 13, 2025
* [SWEP-86] 히스토리 API 임시 저장

* [SWEP-87] 챌린지 API 개선

* [SWEP-58] 스웨거 문서 작성 (#187)

* [SWEP-58] 스웨거 문서 작성

* [SWEP-58] 사진삭제 스웨거 주석 작성

* [SWEP-58] 스웨거 문서 변경

* [SWEP-89] 메모 텍스트 저장 API tsoa 적용 및 여러 multer 객체 사용할 수 있도록 구현 (#189) (#191)

Co-authored-by: JinHa Park <160022452+jjiinaaa@users.noreply.github.com>

* [SWEP-98] tsoa 변환

* [SWEP-98] 임시저장 14:31

* [SWEP-98] app.ts 수정

---------

Co-authored-by: Socializedistp <159607145+Socializedistp@users.noreply.github.com>
Co-authored-by: asjasj3964 <84120715+asjasj3964@users.noreply.github.com>
Co-authored-by: JinHa Park <160022452+jjiinaaa@users.noreply.github.com>
jjiinaaa added a commit that referenced this pull request Feb 14, 2025
* [SWEP-96] 메모장 사진 업로드 및 저장 로직 개선 (#200) (#201)

* [SWEP-96] 메모장 사진 업로드 및 저장 로직 개선 (#200)

* fix: 폴더 생성 및 사진 저장 API 주석처리 + multer 미들웨어 실행 전에 폴더 ID 검사 (#202)

* [SWEP-96] 메모장 사진 업로드 및 저장 로직 개선

* [SWEP-96] 폴더 생성 및 사진 저장 API 주석처리 + multer 미들웨어 실행 전에 폴더 ID 검사

* fix: tsoa.json 폴더 생성 및 사진 저장 spec requestBody 삭제 (#203)

* [SWEP-96] 메모장 사진 업로드 및 저장 로직 개선

* [SWEP-96] 폴더 생성 및 사진 저장 API 주석처리 + multer 미들웨어 실행 전에 폴더 ID 검사

* [SWEP-96] tsoa.json 폴더 생성 및 사진 저장 spec requestBody 삭제

* [SWEP-98] 챌린지 API tsoa 변환 (#205)

* [SWEP-86] 히스토리 API 임시 저장

* [SWEP-87] 챌린지 API 개선

* [SWEP-58] 스웨거 문서 작성 (#187)

* [SWEP-58] 스웨거 문서 작성

* [SWEP-58] 사진삭제 스웨거 주석 작성

* [SWEP-58] 스웨거 문서 변경

* [SWEP-89] 메모 텍스트 저장 API tsoa 적용 및 여러 multer 객체 사용할 수 있도록 구현 (#189) (#191)

Co-authored-by: JinHa Park <160022452+jjiinaaa@users.noreply.github.com>

* [SWEP-98] tsoa 변환

* [SWEP-98] 임시저장 14:31

* [SWEP-98] app.ts 수정

---------

Co-authored-by: Socializedistp <159607145+Socializedistp@users.noreply.github.com>
Co-authored-by: asjasj3964 <84120715+asjasj3964@users.noreply.github.com>
Co-authored-by: JinHa Park <160022452+jjiinaaa@users.noreply.github.com>

---------

Co-authored-by: asjasj3964 <84120715+asjasj3964@users.noreply.github.com>
Co-authored-by: codie0226 <80187674+codie0226@users.noreply.github.com>
Co-authored-by: Socializedistp <159607145+Socializedistp@users.noreply.github.com>
jjiinaaa added a commit that referenced this pull request Feb 15, 2025
* [SWEP-96] 메모장 사진 업로드 및 저장 로직 개선 (#200) (#201)

* [SWEP-96] 메모장 사진 업로드 및 저장 로직 개선 (#200)

* fix: 폴더 생성 및 사진 저장 API 주석처리 + multer 미들웨어 실행 전에 폴더 ID 검사 (#202)

* [SWEP-96] 메모장 사진 업로드 및 저장 로직 개선

* [SWEP-96] 폴더 생성 및 사진 저장 API 주석처리 + multer 미들웨어 실행 전에 폴더 ID 검사

* fix: tsoa.json 폴더 생성 및 사진 저장 spec requestBody 삭제 (#203)

* [SWEP-96] 메모장 사진 업로드 및 저장 로직 개선

* [SWEP-96] 폴더 생성 및 사진 저장 API 주석처리 + multer 미들웨어 실행 전에 폴더 ID 검사

* [SWEP-96] tsoa.json 폴더 생성 및 사진 저장 spec requestBody 삭제

* [SWEP-98] 챌린지 API tsoa 변환 (#205)

* [SWEP-86] 히스토리 API 임시 저장

* [SWEP-87] 챌린지 API 개선

* [SWEP-58] 스웨거 문서 작성 (#187)

* [SWEP-58] 스웨거 문서 작성

* [SWEP-58] 사진삭제 스웨거 주석 작성

* [SWEP-58] 스웨거 문서 변경

* [SWEP-89] 메모 텍스트 저장 API tsoa 적용 및 여러 multer 객체 사용할 수 있도록 구현 (#189) (#191)

Co-authored-by: JinHa Park <160022452+jjiinaaa@users.noreply.github.com>

* [SWEP-98] tsoa 변환

* [SWEP-98] 임시저장 14:31

* [SWEP-98] app.ts 수정

---------

Co-authored-by: Socializedistp <159607145+Socializedistp@users.noreply.github.com>
Co-authored-by: asjasj3964 <84120715+asjasj3964@users.noreply.github.com>
Co-authored-by: JinHa Park <160022452+jjiinaaa@users.noreply.github.com>

* [SWEP-58] 휴지통 API 변경사항 (#207)

* [SWEP-101] 히스토리 API 수정 (#213)

* [SWEP-101] 히스토리 API 수정

* [SWEP-101] 어워드 조회 수정

---------

Co-authored-by: asjasj3964 <84120715+asjasj3964@users.noreply.github.com>
Co-authored-by: codie0226 <80187674+codie0226@users.noreply.github.com>
Co-authored-by: Socializedistp <159607145+Socializedistp@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 FIX 버그 수정 ♻️ REFACTOR 기능 향상 및 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SWEP-89] 메모 텍스트 저장 API tsoa 적용 및 여러 multer 객체 사용할 수 있도록 구현

2 participants