COMMIT EXPLAINER · 4b4abc09

PNG 활동 자료 제한 변경 전 “이름 불일치”에서 변경 후 “저장 전 차단”까지

마지막 업데이트 2026-07-29

이 커밋은 PNG 파일을 시스템에서 없앤 것이 아닙니다. 수업 활동에서 정상 표시를 보장하기 어려운 PNG 참조가 새로 저장되는 길을 목록·편집 화면·API에서 단계적으로 막았습니다.

커밋 4b4abc095ae5 작성일 2026-07-29 대상 커밋일 2026-07-23 13개 파일 · +352 / −17
한 문장 결론

PNG 업로드 자체를 막은 변경은 아닙니다. 활동과 활동 템플릿에서는 PNG 자료를 선택·저장·일괄 등록할 수 없게 하고, 기존 PNG 참조는 사용자가 교체해야 저장되도록 만든 변경입니다.

이번에 제한함

활동·활동 템플릿의 PNG 선택 및 저장

그대로 유지함

PNG 업로드 API, 아바타 자료, 정적 UI 이미지

01

변경 전: 같은 그림인데 이름이 달라졌습니다

파일은 PNG였지만 활동에는 확장자 없는 이름만 저장됐고, 수업 화면은 기본적으로 JPG를 찾았습니다.

실제 자료 image_cat.png
활동에 저장 image_cat
수업 화면이 찾는 이름 image_cat.jpg
실제: .png 요청: .jpg — 이름 불일치

PNG 자료 존재

자료 저장소에는 실제 파일명 image_cat.png가 있습니다.

확장자 제거

/api/resources/list가 선택 목록용 이름을 image_cat으로 만듭니다.

이름만 저장

일반 자료는 resName, 선택형 이미지는 selectionImages에 저장됩니다.

JPG 기본 조회

MeetImage는 확장자가 없으면 .jpg를 붙여 자료를 찾습니다.

표시 실패 가능

실제 PNG와 요청한 JPG 이름이 달라 자료를 표시하지 못할 수 있었습니다.

실패 가능

근거 수준: 위 흐름은 커밋과 현재 렌더러 코드에서 확인되는 실패 가능성입니다. 커밋 메시지에는 특정 수업의 장애 사례가 별도로 기록되어 있지 않으므로, “실제 장애가 확정됐다”고 확대 해석하지 않습니다.

02

변경 후: 한 번 숨기고, 세 번 더 확인합니다

목록에서 예방하고, 기존 데이터는 편집 화면에서 알려주며, 마지막에는 서버가 우회를 차단합니다.

01

1. 목록에서 제외

선택 가능한 resources에서는 PNG를 빼고, 기존 참조 확인용 pngResources에는 이름을 남깁니다.

GET /api/resources/list 새로운 PNG 선택을 예방
02

2. 기존 참조 경고

활동·템플릿 폼이 현재 step을 검사해 “자료 교체가 필요합니다”와 해당 자료명을 보여줍니다.

activity-form · activity-template-form 사용자가 고칠 위치를 알려주는 UX
03

3. 폼 제출 차단

resName이나 selectionImages에 PNG 참조가 남아 있으면 저장 요청 자체를 보내지 않습니다.

create/update submit handler 잘못된 저장을 브라우저에서 즉시 중단
04

4. API 최종 차단

서버가 DynamoDB 자료 목록과 다시 대조하고 HTTP 400 PNG_RESOURCE_NOT_ALLOWED를 반환합니다.

single + bulk API routes 직접 호출·오래된 클라이언트·일괄 우회 방지 차단
UI 검사: 빨리 알려주기 API 검사: 반드시 막기
03

사용자에게는 이렇게 보입니다

같은 제한이지만 새 활동, 기존 활동, API 사용자는 서로 다른 지점에서 안내받습니다.

새 활동 작성자

자료 선택 목록에 PNG 이름이 나타나지 않습니다. 처음부터 JPG·WEBP·GIF 또는 영상 자료를 선택합니다.

체감: 잘못 고를 기회가 사라짐

기존 PNG 활동 편집자

경고에 표시된 PNG 자료명을 확인하고 다른 이미지 또는 영상으로 교체해야 저장할 수 있습니다.

체감: 문제 자료와 해결 행동을 바로 안내

API·일괄 등록 사용자

요청 안에 PNG 참조가 하나라도 있으면 DB 쓰기 전에 HTTP 400으로 거절됩니다.

체감: 조용한 데이터 오염 대신 명시적 오류
중요: 이 커밋은 기존 데이터를 자동 삭제하거나 변환하지 않습니다. 기존 활동을 편집할 때 경고를 보고 자료를 직접 교체하는 방식입니다.
04

무엇이 바뀌고, 무엇은 그대로인가

“PNG 전체 금지”가 아니라 “활동에서 PNG 참조 저장 제한”입니다.

구분 이번 커밋의 처리 의미
활동 / 활동 템플릿 PNG 참조 제한 새 선택과 저장, 기존 참조 재저장을 막음
resName / selectionImages 둘 다 검사 일반 자료와 “선택 후 넘기기” 이미지를 함께 보호
단건 생성·수정 / 일괄 생성·수정 관련 API 모두 서버 검사 화면 밖의 직접 요청도 저장 전에 차단
PNG 업로드 API 유지 .png 업로드 허용 코드는 이번 커밋에서 제거하지 않음
기존 DynamoDB 데이터 자동 삭제·변환 없음 편집 시 경고 후 사람이 교체
아바타·정적 UI 이미지 이번 제한 범위 밖 활동 step 자료 검증과 별개
PNG · 활동에서 제한 JPG / JPEG · 이번 PNG 판정 대상 아님 GIF · 이번 PNG 판정 대상 아님 WEBP · 이번 PNG 판정 대상 아님 MP4 · 이번 PNG 판정 대상 아님

“이번 PNG 판정 대상이 아니다”는 각 형식의 모든 런타임 동작을 이 커밋이 새로 보장한다는 뜻은 아닙니다.

05

개발자 부록: 13개 파일이 만든 방어망

공통 판정 함수 하나를 UI와 서버가 공유하고, 실제 저장 경로마다 서버 가드를 적용했습니다.

분석 대상 4b4abc095ae5f203224466835ddeb84e2e60a351

fix: restrict PNG activity resources

검증 포인트

판정 코어 1 · 서버 가드 1 · API 7 · UI 3 · 테스트 1 = 총 13개 파일

1. 검증 코어

파일명 정규화, PNG 목록, step 참조 교집합을 계산합니다.

  • apps/web/lib/activity-resource-validation.ts

2. 서버 가드

DynamoDB의 실제 자료 파일명과 요청 step을 대조합니다.

  • apps/web/lib/activity-resource-guard.ts

3. API 적용

목록 응답을 분리하고, 활동·템플릿의 단건 및 일괄 저장 직전에 검증합니다.

  • apps/web/app/api/resources/list/route.ts
  • apps/web/app/api/activities/route.ts
  • apps/web/app/api/activities/[userId]/[id]/route.ts
  • apps/web/app/api/activities/bulk/route.ts
  • apps/web/app/api/activity-templates/route.ts
  • apps/web/app/api/activity-templates/[id]/route.ts
  • apps/web/app/api/activity-templates/bulk/route.ts

4. UI 적용

PNG 이름 수신, 기존 참조 경고, 제출 차단과 안내 문구를 담당합니다.

  • apps/web/components/sections/activity-form.tsx
  • apps/web/components/sections/activity-template-form.tsx
  • apps/web/components/ui/step-form.tsx

5. 테스트

대소문자, 이름 정규화, 두 참조 필드 동시 탐지를 고정합니다.

  • apps/web/lib/activity-resource-validation.test.ts

저장 API 커버리지

대상 단건 생성 단건 수정 일괄 생성 일괄 수정
활동 POST /api/activities PUT /api/activities/[userId]/[id] POST /api/activities/bulk PUT /api/activities/bulk
활동 템플릿 POST /api/activity-templates PUT /api/activity-templates/[id] POST /api/activity-templates/bulk 해당 커밋에 별도 일괄 수정 경로 없음

서버가 돌려주는 오류 계약

UI를 거치지 않은 요청도 같은 오류 코드와 실제 문제 자료명을 받습니다. 클라이언트는 resourceNames를 이용해 무엇을 교체할지 안내할 수 있습니다.

HTTP 400 · 차단
{
  "error": "PNG_RESOURCE_NOT_ALLOWED",
  "message": "PNG 자료(image_cat)는 수업 화면에서 사용할 수 없습니다. 다른 이미지 또는 영상으로 교체해주세요.",
  "resourceNames": ["image_cat"]
}

추가된 회귀 테스트 3가지

06

마지막으로 기억할 것

이 커밋의 핵심은 “파일 형식 금지”보다 “잘못된 활동 참조가 저장되지 않게 하는 다층 방어”입니다.

BEFORE → AFTER

변경 전에는 PNG가 활동 목록에 들어오고 확장자 없는 이름으로 저장될 수 있었습니다. 변경 후에는 선택 목록에서 예방하고, 기존 참조를 알려주고, 폼과 서버에서 두 번 차단합니다.

관련 문서