Agency Branding API
에이전시 로고·파비콘 URL 저장 및 파일 업로드
브랜딩/로고·파비콘
에이전시 로고와 파비콘은 두 단계로 처리합니다. 파일을 업로드해 URL을 받고, 그 URL을 PATCH /agency/me/branding으로 저장합니다.
파비콘 규칙
| 저장 상태 | 의미 | 로고를 저장하면 | 응답 faviconUrl |
|---|---|---|---|
| 미설정 | 한 번도 지정하지 않음 | 로고에서 파비콘을 자동 생성해 저장 | 생성된 URL (생성 실패 시 null) |
| 직접 업로드 | faviconUrl에 URL을 저장함 | 유지 (덮어쓰지 않음) | 저장한 URL |
| 자동 생성 | 로고에서 만들어짐 | 로고가 바뀌면 새 로고로 다시 생성 | 생성된 URL |
| 해제 | faviconUrl에 빈 문자열을 저장함 | 생성하지 않음 (아이콘 없음 유지) | null |
- 업로드든 자동 생성이든 이미지는 비율을 유지한 채 투명 정사각 캔버스 가운데에 놓은 180x180 PNG로 변환됩니다.
- 지원 형식: PNG, JPG, GIF, WEBP, SVG. SVG는 스크립트와 외부 참조(외부 이미지, 외부 CSS, 외부 DTD)를 제거한 뒤 래스터화합니다.
- AI, ZIP처럼 이미지가 아닌 로고는 자동 생성하지 않습니다(파비콘 없음).
- 원본은 5MB, 2,500만 픽셀 이하만 받습니다.
PATCH /agency/me/branding은 이미 확보한 로고 URL을 저장하는 API입니다. POST /agency/branding/logo는 multipart 파일을 서버가 받아 S3에 업로드하고 logoUrl을 반환하는 API입니다. presigned URL 전용 구조가 아닙니다.
로고·파비콘 URL 저장
| 항목 | 값 |
|---|---|
| 메서드 | PATCH |
| 경로 | /agency/me/branding 또는 /ai/agency/me/branding |
| 인증 | 필요 |
| 권한 | ROLE_AGENCY |
PATCH /agency/me/branding HTTP/1.1
Host: api.glowb.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: application/json
{
"logoUrl": "https://cdn.example.com/agency/logo.png",
"faviconUrl": "https://d3mp6eqt0w2808.cloudfront.net/business/demo-agency/brand-favicon/1758412800000.png"
}Request Body
Prop
Type
응답
{
"logoUrl": "https://cdn.example.com/agency/logo.png",
"faviconUrl": "https://d3mp6eqt0w2808.cloudfront.net/business/demo-agency/brand-favicon/1758412800000.png"
}로고에서 파비콘을 자동 생성하다 실패해도(이미지 다운로드·변환·업로드 오류 등) 로고 저장은 성공으로 응답하며, faviconUrl은 null로 비워 둡니다. 예전에는 일부 변환 오류가 로고 저장까지 INTERNAL_SERVER_ERROR로 실패시켰습니다.
로고 파일 업로드
이 API는 에이전시 본인 전용입니다. SecurityConfig가 URL 레벨에서 ROLE_ADMIN도 통과시키지만, 서비스가 호출자 본인의 에이전시를 역산하므로(businessType == AGENCY && agency != null) 본인 에이전시가 없는 어드민은 AUTH_002로 거절됩니다. 어드민은 POST /admin/agency/branding/logo를 사용하세요.
| 항목 | 값 |
|---|---|
| 메서드 | POST |
| 경로 | /agency/branding/logo 또는 /ai/agency/branding/logo |
| 인증 | 필요 |
| 권한 | ROLE_AGENCY |
| Content-Type | multipart/form-data |
POST /agency/branding/logo HTTP/1.1
Host: api.glowb.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Content-Type: multipart/form-data; boundary=----boundary
------boundary
Content-Disposition: form-data; name="file"; filename="logo.webp"
Content-Type: image/webp
{binary}
------boundary--curl -X POST "https://api.glowb.com/agency/branding/logo" \
-H "Authorization: Bearer {access_token}" \
-F "file=@logo.webp"const formData = new FormData();
formData.append('file', file);
const upload = await fetch('/agency/branding/logo', {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
},
body: formData,
});
const { logoUrl } = await upload.json();응답
{
"logoUrl": "https://..."
}파비콘 파일 업로드
로고 업로드와 같은 권한 규칙을 따릅니다. 업로드한 이미지는 180x180 PNG로 변환되어 S3에 올라가고, 반환된 faviconUrl을 PATCH /agency/me/branding으로 저장해야 적용됩니다. 어드민의 에이전시 지정 화면은 POST /admin/agency/branding/favicon을 사용합니다.
| 항목 | 값 |
|---|---|
| 메서드 | POST |
| 경로 | /agency/branding/favicon 또는 /ai/agency/branding/favicon |
| 인증 | 필요 |
| 권한 | ROLE_AGENCY |
| Content-Type | multipart/form-data |
curl -X POST "https://api.glowb.com/agency/branding/favicon" \
-H "Authorization: Bearer {access_token}" \
-F "file=@favicon.svg"응답
{
"faviconUrl": "https://d3mp6eqt0w2808.cloudfront.net/business/demo-agency/brand-favicon/1758412800000.png"
}에러 응답
glowb는 CustomException을 HTTP 200으로 감싸 내려보냅니다. 실패 판별은 응답 본문의 status/code로 합니다.
본문 code | 본문 status | 설명 |
|---|---|---|
FILE_EMPTY | 400 | 빈 파일 |
AGENCY_FAVICON_IMAGE_UNSUPPORTED | 400 | 파비콘으로 변환할 수 없는 파일(이미지가 아니거나 손상·조작됨). 손상 파일 일부가 INTERNAL_SERVER_ERROR로 나가던 것을 이 코드로 통일했습니다 |
AGENCY_FAVICON_FILE_TOO_LARGE | 413 | 5MB 초과 (args.maxMb) |
AGENCY_FAVICON_RESOLUTION_TOO_LARGE | 413 | 2,500만 픽셀 초과 |
AGENCY_FAVICON_CONVERT_FAILED | 500 | 지원 형식인데 변환 중 서버 오류 |
FILE_UPLOAD_FAILED | 500 | S3 업로드 실패 |