Skip to content

Latest commit

Β 

History

History
587 lines (421 loc) Β· 18.5 KB

File metadata and controls

587 lines (421 loc) Β· 18.5 KB

파일 API

파일 μ—…λ‘œλ“œ 및 λ‹€μš΄λ‘œλ“œ APIμž…λ‹ˆλ‹€. AWS S3λ₯Ό μ‚¬μš©ν•˜μ—¬ μ•„μΉ΄μ΄λΈŒ 파일, λ¬Έμ„œ, λ°±μ—… 파일, κ°•μ˜κ³„νšμ„œ 컀버 이미지, AI 참고자료 λ“±μ˜ μ—…λ‘œλ“œ/λ‹€μš΄λ‘œλ“œλ₯Ό κ΄€λ¦¬ν•©λ‹ˆλ‹€. Pre-Signed URL을 톡해 λ³΄μ•ˆλœ 파일 접근을 μ œκ³΅ν•©λ‹ˆλ‹€.

라우트 파일: backend/src/routes/files.js 컨트둀러 파일: backend/src/controllers/files.js S3 μ„€μ • 파일: backend/src/_s3/fileBucket.js, backend/src/_s3/profileBucket.js


μ—”λ“œν¬μΈνŠΈ μš”μ•½

λ©”μ„œλ“œ 경둜 μ„€λͺ… κΆŒν•œ
POST /api/files/archive μ•„μΉ΄μ΄λΈŒ 파일 μ—…λ‘œλ“œ isLoggedIn
GET /api/files/archive/signed μ•„μΉ΄μ΄λΈŒ 파일 μ„œλͺ… URL 쑰회 isLoggedIn
GET /api/files/document/signed λ¬Έμ„œ 파일 μ„œλͺ… URL 쑰회 isLoggedIn
GET /api/files/backup/signed λ°±μ—… 파일 μ„œλͺ… URL 쑰회 owner|admin

κ΄€λ ¨ 파일 μ—”λ“œν¬μΈνŠΈ (λ‹€λ₯Έ 라우트)

λ©”μ„œλ“œ 경둜 μ„€λͺ… κΆŒν•œ
PUT /api/syllabuses/:_id/cover-image κ°•μ˜κ³„νšμ„œ 컀버 이미지 μ—…λ‘œλ“œ isLoggedIn
DELETE /api/syllabuses/:_id/cover-image κ°•μ˜κ³„νšμ„œ 컀버 이미지 μ‚­μ œ isLoggedIn
POST /api/seasons/:_id/ai/reference/upload AI 참고자료 μ—…λ‘œλ“œ admin|manager
GET /api/seasons/:_id/ai/reference/:index/download AI 참고자료 λ‹€μš΄λ‘œλ“œ admin|manager
DELETE /api/seasons/:_id/ai/reference/:index AI 참고자료 μ‚­μ œ admin|manager

S3 버킷 ꡬ성

μ‹œμŠ€ν…œμ€ 두 개의 S3 버킷을 μ‚¬μš©ν•©λ‹ˆλ‹€.

버킷 ν™˜κ²½λ³€μˆ˜ μš©λ„
fileBucket s3_bucket2 μ•„μΉ΄μ΄λΈŒ, μ±„νŒ… 파일, AI 참고자료
profileBucket s3_bucket ν”„λ‘œν•„ 이미지, κ°•μ˜κ³„νšμ„œ 컀버 이미지 (Lambda 썸넀일 생성)
  • λͺ¨λ“  μ—…λ‘œλ“œμ— ACL: public-read 적용
  • Pre-Signed URL을 톡해 λ³΄μ•ˆλœ μž„μ‹œ λ‹€μš΄λ‘œλ“œ μ ‘κ·Ό 제곡

S3 ν‚€ νŒ¨ν„΄

μœ ν˜• νŒ¨ν„΄ μ˜ˆμ‹œ
μ•„μΉ΄μ΄λΈŒ {academyId}/archive/{timestamp}_{random}.{ext} my-academy/archive/1697012345_aBcDeFgHiJkL.pdf
μ±„νŒ… {academyId}/chat/{roomId}/{timestamp}_{random}.{ext} my-academy/chat/room01/1697012345_aBcDeFgHiJkL.jpg
AI 참고자료 {academyId}/ai-ref/{seasonId}/{timestamp}_{random}.{ext} my-academy/ai-ref/507f1f77bcf8/1697012345_aBcDeFgHiJkL.pdf
컀버 이미지 original/{academyId}/courses/{syllabusId}/{filename} original/my-academy/courses/507f1f77bcf8/507f1f77bcf86cd7.jpg
ν”„λ‘œν•„ 이미지 original/{academyId}/{userId}/{filename} original/my-academy/507f1f77bcf8/507f1f77bcf86cd7.jpg

μ°Έκ³ : 컀버 μ΄λ―Έμ§€μ˜ 경우, original/ κ²½λ‘œμ— μ—…λ‘œλ“œν•˜λ©΄ AWS Lambdaκ°€ μžλ™μœΌλ‘œ thumb/ κ²½λ‘œμ— 썸넀일을 μƒμ„±ν•©λ‹ˆλ‹€.


μ•„μΉ΄μ΄λΈŒ 파일 μ—…λ‘œλ“œ

μ•„μΉ΄μ΄λΈŒμ— νŒŒμΌμ„ μ—…λ‘œλ“œν•©λ‹ˆλ‹€. multipart/form-data ν˜•μ‹μœΌλ‘œ νŒŒμΌμ„ μ „μ†‘ν•˜λ©°, S3에 μ—…λ‘œλ“œ ν›„ Pre-Signed URLκ³Ό ν•¨κ»˜ κ²°κ³Όλ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.

POST /api/files/archive

κΆŒν•œ: isLoggedIn (둜그인 ν•„μš”)

Content-Type: multipart/form-data

μš”μ²­ λ³Έλ¬Έ (FormData)

ν•„λ“œ νƒ€μž… ν•„μˆ˜ μ„€λͺ…
file File O μ—…λ‘œλ“œν•  파일

파일 μ œν•œ

ν•­λͺ© μ œν•œ
μ΅œλŒ€ 크기 5MB
μ΅œλŒ€ 파일 수 1개
ν—ˆμš© MIME νƒ€μž… image/png, image/jpeg, image/jpg, image/webp, application/pdf, application/vnd.hancom.hwp, application/vnd.openxmlformats-officedocument.wordprocessingml.document (docx), application/vnd.openxmlformats-officedocument.spreadsheetml.sheet (xlsx), application/zip, application/octet-stream

응닡 (200)

{
  "originalName": "λ³΄κ³ μ„œ.pdf",
  "type": "application/pdf",
  "key": "my-academy/archive/1697012345_aBcDeFgHiJkL.pdf",
  "url": "https://s3.ap-northeast-2.amazonaws.com/bucket/my-academy/archive/1697012345_aBcDeFgHiJkL.pdf",
  "preSignedUrl": "https://s3.ap-northeast-2.amazonaws.com/bucket/my-academy/archive/1697012345_aBcDeFgHiJkL.pdf?X-Amz-Algorithm=...",
  "expiryDate": "2024-01-15T09:05:00.000Z"
}

μ—λŸ¬ 응닡

μƒνƒœ μ½”λ“œ λ©”μ‹œμ§€ μ„€λͺ…
409 LIMIT_FILE_SIZE 파일 크기가 5MBλ₯Ό 초과
409 INVALID_FILE_TYPE ν—ˆμš©λ˜μ§€ μ•ŠλŠ” 파일 νƒ€μž…

μ•„μΉ΄μ΄λΈŒ 파일 μ„œλͺ… URL 쑰회

μ•„μΉ΄μ΄λΈŒ 파일의 λ‹€μš΄λ‘œλ“œλ₯Ό μœ„ν•œ Pre-Signed URL을 μƒμ„±ν•©λ‹ˆλ‹€. μ•„μΉ΄μ΄λΈŒ λ°μ΄ν„°μ˜ 쑴재 여뢀와 μ‚¬μš©μžμ˜ μ ‘κ·Ό κΆŒν•œμ„ κ²€μ¦ν•œ ν›„ URL을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

GET /api/files/archive/signed

κΆŒν•œ: isLoggedIn (둜그인 ν•„μš”)

쿼리 νŒŒλΌλ―Έν„°

νŒŒλΌλ―Έν„° νƒ€μž… ν•„μˆ˜ μ„€λͺ…
key string O S3 파일 ν‚€ (archive/둜 μ‹œμž‘ν•΄μ•Ό 함)
archive string O μ•„μΉ΄μ΄λΈŒ ObjectId
label string O μ•„μΉ΄μ΄λΈŒ λ°μ΄ν„°μ˜ 라벨 (양식 ν•­λͺ©λͺ…)
fieldLabel string O μ•„μΉ΄μ΄λΈŒ ν•„λ“œ 라벨 (ν•„λ“œ ν•­λͺ©λͺ…)
fileName string O λ‹€μš΄λ‘œλ“œ μ‹œ 파일λͺ…

κΆŒν•œ 검증 둜직

μ•„μΉ΄μ΄λΈŒ 파일 μ ‘κ·Ό μ‹œ, ν•΄λ‹Ή ν•™κ΅μ˜ 양식(formArchive) 섀정에 따라 역할별 κΆŒν•œμ„ κ²€μ¦ν•©λ‹ˆλ‹€.

μ—­ν•  쑰건 μ„€λͺ…
manager authManager === "viewAndEdit" λ§€λ‹ˆμ € κΆŒν•œμœΌλ‘œ λ°”λ‘œ μ ‘κ·Ό κ°€λŠ₯
본인(학생) authStudent === "view" λ˜λŠ” "viewAndEdit" μžμ‹ μ˜ μ•„μΉ΄μ΄λΈŒ 데이터 μ ‘κ·Ό κ°€λŠ₯
ꡐ사 (전체) authTeacher === "viewAndEditStudents" 같은 학ꡐ에 λ“±λ‘λœ ꡐ사인 경우 μ ‘κ·Ό κ°€λŠ₯
ꡐ사 (λ‹΄λ‹Ή) authTeacher === "viewAndEditMyStudents" ν•΄λ‹Ή ν•™μƒμ˜ λ‹΄λ‹Ή ꡐ사 λ˜λŠ” λΆ€λ‹΄λ‹Ή ꡐ사인 경우만 μ ‘κ·Ό κ°€λŠ₯

μš”μ²­ μ˜ˆμ‹œ

GET /api/files/archive/signed?key=my-academy/archive/1697012345_aBcDeFgHiJkL.pdf&archive=507f1f77bcf86cd799439011&label=μ„±μ ν‘œ&fieldLabel=μ²¨λΆ€νŒŒμΌ&fileName=μ„±μ ν‘œ_첨뢀.pdf

응닡 (200)

{
  "preSignedUrl": "https://s3.ap-northeast-2.amazonaws.com/bucket/my-academy/archive/1697012345_aBcDeFgHiJkL.pdf?X-Amz-Algorithm=...",
  "expiryDate": "2024-01-15T09:01:00.000Z"
}

μ°Έκ³ : URL 유효 기간은 60μ΄ˆμž…λ‹ˆλ‹€.

μ—λŸ¬ 응닡

μƒνƒœ μ½”λ“œ λ©”μ‹œμ§€ μ„€λͺ…
400 FIELD_REQUIRED({ν•„λ“œλͺ…}) ν•„μˆ˜ 쿼리 νŒŒλΌλ―Έν„° λˆ„λ½
400 FIELD_INVALID(key) ν‚€κ°€ archive/둜 μ‹œμž‘ν•˜μ§€ μ•ŠμŒ
403 PERMISSION_DENIED μ ‘κ·Ό κΆŒν•œ μ—†μŒ
404 ARCHIVE_NOT_FOUND μ•„μΉ΄μ΄λΈŒλ₯Ό 찾을 수 μ—†μŒ
404 SCHOOL_NOT_FOUND 학ꡐλ₯Ό 찾을 수 μ—†μŒ
404 FORMITEM_NOT_FOUND 양식 ν•­λͺ©μ„ 찾을 수 μ—†μŒ
404 FORMITEMFIELD_NOT_FOUND 양식 ν•„λ“œλ₯Ό 찾을 수 μ—†μŒ

λ¬Έμ„œ 파일 μ„œλͺ… URL 쑰회

λ¬Έμ„œ 파일의 λ‹€μš΄λ‘œλ“œλ₯Ό μœ„ν•œ Pre-Signed URL을 μƒμ„±ν•©λ‹ˆλ‹€. λ³„λ„μ˜ λ¬Έμ„œ 쑴재 검증 없이 킀와 파일λͺ…λ§ŒμœΌλ‘œ μ„œλͺ…λœ URL을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

GET /api/files/document/signed

κΆŒν•œ: isLoggedIn (둜그인 ν•„μš”)

쿼리 νŒŒλΌλ―Έν„°

νŒŒλΌλ―Έν„° νƒ€μž… ν•„μˆ˜ μ„€λͺ…
key string O S3 파일 ν‚€
fileName string O λ‹€μš΄λ‘œλ“œ μ‹œ 파일λͺ…

μš”μ²­ μ˜ˆμ‹œ

GET /api/files/document/signed?key=my-academy/document/report.pdf&fileName=λ³΄κ³ μ„œ.pdf

응닡 (200)

{
  "preSignedUrl": "https://s3.ap-northeast-2.amazonaws.com/bucket/my-academy/document/report.pdf?X-Amz-Algorithm=...",
  "expiryDate": "2024-01-15T09:01:00.000Z"
}

μ°Έκ³ : URL 유효 기간은 60μ΄ˆμž…λ‹ˆλ‹€.

μ—λŸ¬ 응닡

μƒνƒœ μ½”λ“œ λ©”μ‹œμ§€ μ„€λͺ…
400 FIELD_REQUIRED({ν•„λ“œλͺ…}) ν•„μˆ˜ 쿼리 νŒŒλΌλ―Έν„° λˆ„λ½ (key λ˜λŠ” fileName)

λ°±μ—… 파일 μ„œλͺ… URL 쑰회

λ°±μ—… 파일의 λ‹€μš΄λ‘œλ“œλ₯Ό μœ„ν•œ Pre-Signed URL을 μƒμ„±ν•©λ‹ˆλ‹€. owner λ˜λŠ” admin κΆŒν•œμ΄ ν•„μš”ν•˜λ©°, ν‚€κ°€ backup/으둜 μ‹œμž‘ν•˜λŠ”μ§€ κ²€μ¦ν•©λ‹ˆλ‹€.

GET /api/files/backup/signed

κΆŒν•œ: owner λ˜λŠ” admin

쿼리 νŒŒλΌλ―Έν„°

νŒŒλΌλ―Έν„° νƒ€μž… ν•„μˆ˜ μ„€λͺ…
key string O S3 파일 ν‚€ (backup/으둜 μ‹œμž‘ν•΄μ•Ό 함)
fileName string O λ‹€μš΄λ‘œλ“œ μ‹œ 파일λͺ…

μš”μ²­ μ˜ˆμ‹œ

GET /api/files/backup/signed?key=my-academy/backup/2024-01-15_09:00:00.000/users.json&fileName=users.json

응닡 (200)

{
  "preSignedUrl": "https://s3.ap-northeast-2.amazonaws.com/bucket/my-academy/backup/2024-01-15_09:00:00.000/users.json?X-Amz-Algorithm=...",
  "expiryDate": "2024-01-15T09:01:00.000Z"
}

μ°Έκ³ : URL 유효 기간은 60μ΄ˆμž…λ‹ˆλ‹€.

μ—λŸ¬ 응닡

μƒνƒœ μ½”λ“œ λ©”μ‹œμ§€ μ„€λͺ…
400 FIELD_REQUIRED({ν•„λ“œλͺ…}) ν•„μˆ˜ 쿼리 νŒŒλΌλ―Έν„° λˆ„λ½ (key λ˜λŠ” fileName)
400 FIELD_INVALID(key) ν‚€κ°€ backup/으둜 μ‹œμž‘ν•˜μ§€ μ•ŠμŒ
403 PERMISSION_DENIED owner λ˜λŠ” admin κΆŒν•œ μ—†μŒ

κ°•μ˜κ³„νšμ„œ 컀버 이미지 μ—…λ‘œλ“œ

κ°•μ˜κ³„νšμ„œμ˜ 컀버 이미지λ₯Ό μ—…λ‘œλ“œν•©λ‹ˆλ‹€. profileBucket의 original/ κ²½λ‘œμ— μ—…λ‘œλ“œλ˜λ©°, AWS Lambdaκ°€ μžλ™μœΌλ‘œ thumb/ κ²½λ‘œμ— 썸넀일을 μƒμ„±ν•©λ‹ˆλ‹€.

PUT /api/syllabuses/:_id/cover-image

κΆŒν•œ: isLoggedIn (둜그인 ν•„μš”) - κ°•μ˜κ³„νšμ„œ μƒμ„±μž, λ‹΄λ‹Ή ꡐ사, λ˜λŠ” manager

Content-Type: multipart/form-data

라우트 파일: backend/src/routes/syllabuses.js 컨트둀러 파일: backend/src/controllers/syllabuses.js

경둜 νŒŒλΌλ―Έν„°

νŒŒλΌλ―Έν„° νƒ€μž… μ„€λͺ…
_id string κ°•μ˜κ³„νšμ„œ ObjectId

μš”μ²­ λ³Έλ¬Έ (FormData)

ν•„λ“œ νƒ€μž… ν•„μˆ˜ μ„€λͺ…
img File O μ—…λ‘œλ“œν•  이미지 파일

파일 μ œν•œ

ν•­λͺ© μ œν•œ
μ΅œλŒ€ 크기 2MB
μ΅œλŒ€ 파일 수 1개
ν—ˆμš© MIME νƒ€μž… image/png, image/jpeg, image/jpg, image/webp

응닡 (200)

{
  "coverImage": "https://s3.ap-northeast-2.amazonaws.com/bucket/thumb/my-academy/courses/507f1f77bcf86cd799439011/507f1f77bcf86cd7.jpg"
}

μ°Έκ³ : μ‘λ‹΅μ˜ coverImageλŠ” thumb/ 경둜의 썸넀일 URLμž…λ‹ˆλ‹€. original/ κ²½λ‘œμ— μ—…λ‘œλ“œλœ 원본 μ΄λ―Έμ§€μ—μ„œ μžλ™ λ³€ν™˜λ©λ‹ˆλ‹€.

μ—λŸ¬ 응닡

μƒνƒœ μ½”λ“œ λ©”μ‹œμ§€ μ„€λͺ…
403 PERMISSION_DENIED μƒμ„±μž, λ‹΄λ‹Ή ꡐ사, λ§€λ‹ˆμ €κ°€ μ•„λ‹Œ 경우
404 SYLLABUS_NOT_FOUND κ°•μ˜κ³„νšμ„œλ₯Ό 찾을 수 μ—†μŒ
409 LIMIT_FILE_SIZE 파일 크기가 2MBλ₯Ό 초과
409 INVALID_FILE_TYPE ν—ˆμš©λ˜μ§€ μ•ŠλŠ” 파일 νƒ€μž… (이미지 μ™Έ)

κ°•μ˜κ³„νšμ„œ 컀버 이미지 μ‚­μ œ

κ°•μ˜κ³„νšμ„œμ˜ 컀버 이미지λ₯Ό μ‚­μ œν•©λ‹ˆλ‹€.

DELETE /api/syllabuses/:_id/cover-image

κΆŒν•œ: isLoggedIn (둜그인 ν•„μš”) - κ°•μ˜κ³„νšμ„œ μƒμ„±μž, λ‹΄λ‹Ή ꡐ사, λ˜λŠ” manager

라우트 파일: backend/src/routes/syllabuses.js 컨트둀러 파일: backend/src/controllers/syllabuses.js

경둜 νŒŒλΌλ―Έν„°

νŒŒλΌλ―Έν„° νƒ€μž… μ„€λͺ…
_id string κ°•μ˜κ³„νšμ„œ ObjectId

응닡 (200)

{}

μ—λŸ¬ 응닡

μƒνƒœ μ½”λ“œ λ©”μ‹œμ§€ μ„€λͺ…
403 PERMISSION_DENIED μƒμ„±μž, λ‹΄λ‹Ή ꡐ사, λ§€λ‹ˆμ €κ°€ μ•„λ‹Œ 경우
404 SYLLABUS_NOT_FOUND κ°•μ˜κ³„νšμ„œλ₯Ό 찾을 수 μ—†μŒ

AI 참고자료 μ—…λ‘œλ“œ

ν•™κΈ°(Season)의 AI 섀정에 참고자료 νŒŒμΌμ„ μ—…λ‘œλ“œν•©λ‹ˆλ‹€. νŒŒμΌμ€ S3에 μ €μž₯되며, μ—…λ‘œλ“œ ν›„ ν…μŠ€νŠΈ λ‚΄μš©μ„ μžλ™ μΆ”μΆœν•˜μ—¬ AI μ»¨ν…μŠ€νŠΈλ‘œ ν™œμš©ν•©λ‹ˆλ‹€.

POST /api/seasons/:_id/ai/reference/upload

κΆŒν•œ: admin λ˜λŠ” manager

Content-Type: multipart/form-data

라우트 파일: backend/src/routes/seasons.js 컨트둀러 파일: backend/src/controllers/seasons.js

경둜 νŒŒλΌλ―Έν„°

νŒŒλΌλ―Έν„° νƒ€μž… μ„€λͺ…
_id string ν•™κΈ° ObjectId

μš”μ²­ λ³Έλ¬Έ (FormData)

ν•„λ“œ νƒ€μž… ν•„μˆ˜ μ„€λͺ…
file File O μ—…λ‘œλ“œν•  참고자료 파일
title string X 참고자료 제λͺ© (미제곡 μ‹œ 파일λͺ… μ‚¬μš©)

파일 μ œν•œ

ν•­λͺ© μ œν•œ
μ΅œλŒ€ 크기 10MB
μ΅œλŒ€ 파일 수 1개
ν—ˆμš© MIME νƒ€μž… application/pdf, application/vnd.hancom.hwp, application/vnd.openxmlformats-officedocument.wordprocessingml.document (docx), text/plain, application/octet-stream

응닡 (200)

{
  "season": {
    "_id": "507f1f77bcf86cd799439011",
    "aiSettings": {
      "enabled": true,
      "permission": {
        "teacher": true,
        "student": false
      },
      "guidelines": "학생 성적 뢄석에 μ°Έκ³ ν•˜μ„Έμš”.",
      "references": [
        {
          "title": "κ΅μœ‘κ³Όμ • κ°€μ΄λ“œ",
          "content": "μΆ”μΆœλœ ν…μŠ€νŠΈ λ‚΄μš©...",
          "fileName": "κ΅μœ‘κ³Όμ •_κ°€μ΄λ“œ.pdf",
          "fileKey": "my-academy/ai-ref/507f1f77bcf86cd799439011/1697012345_aBcDeFgHiJkL.pdf",
          "fileSize": 1048576,
          "mimeType": "application/pdf"
        }
      ]
    }
  }
}

μ—λŸ¬ 응닡

μƒνƒœ μ½”λ“œ λ©”μ‹œμ§€ μ„€λͺ…
400 파일 ν¬κΈ°λŠ” 10MBλ₯Ό μ΄ˆκ³Όν•  수 μ—†μŠ΅λ‹ˆλ‹€. 파일 크기 초과
400 μ§€μ›ν•˜μ§€ μ•ŠλŠ” 파일 ν˜•μ‹μž…λ‹ˆλ‹€. PDF, DOCX, TXT, HWP 파일만 μ—…λ‘œλ“œν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν—ˆμš©λ˜μ§€ μ•ŠλŠ” 파일 νƒ€μž…
400 FIELD_REQUIRED(file) 파일이 μ²¨λΆ€λ˜μ§€ μ•ŠμŒ
404 SEASON_NOT_FOUND ν•™κΈ°λ₯Ό 찾을 수 μ—†μŒ

AI 참고자료 λ‹€μš΄λ‘œλ“œ

학기에 λ“±λ‘λœ AI 참고자료 파일의 λ‹€μš΄λ‘œλ“œλ₯Ό μœ„ν•œ Pre-Signed URL을 μƒμ„±ν•©λ‹ˆλ‹€.

GET /api/seasons/:_id/ai/reference/:index/download

κΆŒν•œ: admin λ˜λŠ” manager

라우트 파일: backend/src/routes/seasons.js 컨트둀러 파일: backend/src/controllers/seasons.js

경둜 νŒŒλΌλ―Έν„°

νŒŒλΌλ―Έν„° νƒ€μž… μ„€λͺ…
_id string ν•™κΈ° ObjectId
index number 참고자료 λ°°μ—΄ 인덱슀 (0λΆ€ν„° μ‹œμž‘)

응닡 (200)

{
  "url": "https://s3.ap-northeast-2.amazonaws.com/bucket/my-academy/ai-ref/507f1f77bcf8/1697012345_aBcDeFgHiJkL.pdf?X-Amz-Algorithm=..."
}

μ°Έκ³ : URL 유효 기간은 300초(5λΆ„)μž…λ‹ˆλ‹€.

μ—λŸ¬ 응닡

μƒνƒœ μ½”λ“œ λ©”μ‹œμ§€ μ„€λͺ…
404 SEASON_NOT_FOUND ν•™κΈ°λ₯Ό 찾을 수 μ—†μŒ
404 REFERENCE FILE_NOT_FOUND ν•΄λ‹Ή 인덱슀의 참고자료λ₯Ό 찾을 수 μ—†μŒ

AI 참고자료 μ‚­μ œ

학기에 λ“±λ‘λœ AI 참고자료λ₯Ό μ‚­μ œν•©λ‹ˆλ‹€. S3의 파일과 ν•™κΈ° λ°μ΄ν„°μ—μ„œ λͺ¨λ‘ μ‚­μ œλ©λ‹ˆλ‹€.

DELETE /api/seasons/:_id/ai/reference/:index

κΆŒν•œ: admin λ˜λŠ” manager

라우트 파일: backend/src/routes/seasons.js 컨트둀러 파일: backend/src/controllers/seasons.js

경둜 νŒŒλΌλ―Έν„°

νŒŒλΌλ―Έν„° νƒ€μž… μ„€λͺ…
_id string ν•™κΈ° ObjectId
index number 참고자료 λ°°μ—΄ 인덱슀 (0λΆ€ν„° μ‹œμž‘)

응닡 (200)

{
  "season": {
    "_id": "507f1f77bcf86cd799439011",
    "aiSettings": {
      "enabled": true,
      "permission": {
        "teacher": true,
        "student": false
      },
      "guidelines": "학생 성적 뢄석에 μ°Έκ³ ν•˜μ„Έμš”.",
      "references": []
    }
  }
}

μ—λŸ¬ 응닡

μƒνƒœ μ½”λ“œ λ©”μ‹œμ§€ μ„€λͺ…
404 SEASON_NOT_FOUND ν•™κΈ°λ₯Ό 찾을 수 μ—†μŒ
404 REFERENCE_NOT_FOUND ν•΄λ‹Ή 인덱슀의 참고자료λ₯Ό 찾을 수 μ—†μŒ

Pre-Signed URL λ©”μ»€λ‹ˆμ¦˜

Pre-Signed URL은 S3 νŒŒμΌμ— λŒ€ν•œ μž„μ‹œ μ ‘κ·Ό κΆŒν•œμ„ μ œκ³΅ν•©λ‹ˆλ‹€.

URL 생성 방식

signUrl(key, filename, seconds)
νŒŒλΌλ―Έν„° κΈ°λ³Έκ°’ μ„€λͺ…
key - S3 파일 ν‚€
filename - λ‹€μš΄λ‘œλ“œ μ‹œ 파일λͺ… (Content-Disposition 헀더에 μ‚¬μš©)
seconds 300 URL 유효 κΈ°κ°„ (초)

μ—”λ“œν¬μΈνŠΈλ³„ URL 유효 κΈ°κ°„

μ—”λ“œν¬μΈνŠΈ 유효 κΈ°κ°„
POST /files/archive (μ—…λ‘œλ“œ 응닡) 300초 (5λΆ„, κΈ°λ³Έκ°’)
GET /files/archive/signed 60초
GET /files/document/signed 60초
GET /files/backup/signed 60초
GET /seasons/:_id/ai/reference/:index/download 300초 (5λΆ„)

응닡 ꡬ쑰

Pre-Signed URL μ‘λ‹΅μ—λŠ” λ‹€μŒμ΄ ν¬ν•¨λ©λ‹ˆλ‹€:

  • preSignedUrl: μ„œλͺ…λœ S3 URL (λ‹€μš΄λ‘œλ“œ μ‹œ Content-Disposition: attachment 헀더 포함)
  • expiryDate: URL 만료 μ‹œκ° (ISO 8601 ν˜•μ‹)

ν”„λ‘ νŠΈμ—”λ“œ API ν•¨μˆ˜ (useAPIv2)

ν•¨μˆ˜λͺ… λ©”μ„œλ“œ 경둜 μ„€λͺ…
CUploadFileArchive POST /api/files/archive μ•„μΉ΄μ΄λΈŒ 파일 μ—…λ‘œλ“œ
RSignedUrlArchive GET /api/files/archive/signed μ•„μΉ΄μ΄λΈŒ μ„œλͺ… URL 쑰회
RSignedUrlDocument GET /api/files/document/signed λ¬Έμ„œ μ„œλͺ… URL 쑰회
RSignedUrlBackup GET /api/files/backup/signed λ°±μ—… μ„œλͺ… URL 쑰회

μ‚¬μš© μ˜ˆμ‹œ

const { FileAPI } = useAPIv2();

// μ•„μΉ΄μ΄λΈŒ 파일 μ—…λ‘œλ“œ
const formData = new FormData();
formData.append("file", file);
const result = await FileAPI.CUploadFileArchive({ data: formData });
// result: { originalName, key, url, preSignedUrl, expiryDate }

// μ•„μΉ΄μ΄λΈŒ 파일 μ„œλͺ… URL 쑰회
const { preSignedUrl, expiryDate } = await FileAPI.RSignedUrlArchive({
  query: {
    key: "my-academy/archive/1697012345_aBcDeFgHiJkL.pdf",
    archive: "507f1f77bcf86cd799439011",
    label: "μ„±μ ν‘œ",
    fieldLabel: "μ²¨λΆ€νŒŒμΌ",
    fileName: "μ„±μ ν‘œ_첨뢀.pdf",
  },
});

// λ¬Έμ„œ 파일 μ„œλͺ… URL 쑰회
const { preSignedUrl, expiryDate } = await FileAPI.RSignedUrlDocument({
  query: {
    key: "my-academy/document/report.pdf",
    fileName: "λ³΄κ³ μ„œ.pdf",
  },
});

// λ°±μ—… 파일 μ„œλͺ… URL 쑰회
const { preSignedUrl, expiryDate } = await FileAPI.RSignedUrlBackup({
  query: {
    key: "my-academy/backup/2024-01-15_09:00:00.000/users.json",
    fileName: "users.json",
  },
});