From 21d01632be75f82681f3e56e610f79ef09f17c77 Mon Sep 17 00:00:00 2001 From: zenn Date: Tue, 2 Jun 2026 17:47:15 +0900 Subject: [PATCH] =?UTF-8?q?SNS=20=EB=A7=81=ED=81=AC=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=20=EB=B3=B5=EA=B5=AC=20v1.5.41?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../049_fix_social_links_jsonb_string.sql | 5 +++++ docs/changelog.md | 5 +++++ docs/deploy.md | 7 ++++++- docs/history.md | 4 ++++ docs/map.md | 2 +- docs/spec.md | 2 +- docs/update.md | 6 ++++++ lib/social-links.js | 8 +++++++ package-lock.json | 4 ++-- package.json | 2 +- pages/admin/settings/index.vue | 21 ++++--------------- server/repositories/content-repository.js | 2 +- 12 files changed, 44 insertions(+), 24 deletions(-) create mode 100644 db/migrations/049_fix_social_links_jsonb_string.sql diff --git a/db/migrations/049_fix_social_links_jsonb_string.sql b/db/migrations/049_fix_social_links_jsonb_string.sql new file mode 100644 index 0000000..f7f98d9 --- /dev/null +++ b/db/migrations/049_fix_social_links_jsonb_string.sql @@ -0,0 +1,5 @@ +UPDATE site_settings +SET social_links = (social_links #>> '{}')::jsonb +WHERE jsonb_typeof(social_links) = 'string' + AND (social_links #>> '{}') IS NOT NULL + AND (social_links #>> '{}') ~ '^\s*\['; diff --git a/docs/changelog.md b/docs/changelog.md index ec5e2fc..bfe8fb2 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,10 @@ # 업데이트 요약 +## v1.5.41 + +- SNS 링크가 저장 후 사라져 보이던 문제를 수정했다. +- SNS 링크 편집 화면을 아이콘과 주소 중심으로 단순화했다. + ## v1.5.40 - SNS 링크 주소 입력 시 `https://`를 생략해도 자동으로 보정된다. diff --git a/docs/deploy.md b/docs/deploy.md index e42b855..5dcaa4f 100644 --- a/docs/deploy.md +++ b/docs/deploy.md @@ -1,6 +1,6 @@ # 배포 가이드 -> 로컬 기준 v1.5.40에서 `npm run lint`, `npm run build` 검증을 통과했다. NAS 실제 컨테이너 기동과 도메인/프록시 접속 검증은 운영 배포 단계에서 진행한다. +> 로컬 기준 v1.5.41에서 `npm run lint`, `npm run build` 검증을 통과했다. NAS 실제 컨테이너 기동과 도메인/프록시 접속 검증은 운영 배포 단계에서 진행한다. ## 빌드 유형 @@ -68,6 +68,11 @@ docker exec sori-studio-db pg_isready -U sori_studio -d sori_studio docker exec sori-studio-db psql -U sori_studio -d sori_studio -c 'SELECT count(*) AS posts_count FROM posts;' ``` +### v1.5.41 마이그레이션 + +- `049_fix_social_links_jsonb_string.sql`: 기존에 JSONB 문자열로 잘못 저장된 `site_settings.social_links` 값을 JSONB 배열로 복구한다. +- 적용 후 관리자 사이트 설정의 SNS 정보 저장값이 읽기 모드에서 유지되는지 확인한다. + ### v1.5.40 참고 - 추가 DB 마이그레이션은 없다. diff --git a/docs/history.md b/docs/history.md index 84a2c93..3e99439 100644 --- a/docs/history.md +++ b/docs/history.md @@ -1,5 +1,9 @@ # 의사결정 이력 +## 2026-06-02 v1.5.41 — SNS 편집은 아이콘과 주소만으로 충분하다 + +FOLLOW 영역은 아이콘 버튼 목록이므로 운영자가 직접 보는 편집 화면에서도 핵심 입력은 아이콘과 주소다. 이름은 접근성 라벨로 내부에서 프리셋명을 쓰면 충분하고, 별도 입력칸은 레이아웃을 복잡하게 만든다. 저장값은 JSONB 배열이어야 하므로 기존 문자열 저장값은 마이그레이션으로 복구하고, 저장 쿼리도 명시적으로 JSONB 배열로 캐스팅한다. + ## 2026-06-02 v1.5.40 — SNS 아이콘은 프리셋과 직접 SVG를 함께 둔다 SNS 채널은 서비스 유행에 따라 계속 바뀌므로, Facebook·LinkedIn 같은 고정 필드만 두면 운영자가 쓰지 않는 항목과 새로 필요한 항목 사이에서 계속 코드 수정이 필요해진다. 기본 아이콘은 프리셋으로 빠르게 선택하게 두되, 프리셋에 없는 서비스는 관리자가 SVG 아이콘을 직접 붙여 넣어 같은 목록 구조로 저장하도록 했다. URL은 운영자가 `https://`를 생략해도 정상 링크로 보정해 저장 실패처럼 보이는 혼란을 줄인다. diff --git a/docs/map.md b/docs/map.md index 9886e3c..34bbcaf 100644 --- a/docs/map.md +++ b/docs/map.md @@ -37,7 +37,7 @@ | lib/analytics-shared.js | 통계 추적 경로 필터·체류/스크롤 상수(클라이언트·서버 공용) | | lib/analytics.js | 서버 전용 visitor/session hash(`node:crypto`) | | lib/analytics-traffic.js | referrer·User-Agent 기반 유입원·디바이스·검색 키워드 축약 분류 | -| lib/social-links.js | 사이트 설정 SNS 링크 아이콘 프리셋·사용자 SVG 정리·URL 자동 보정·공개 노출 목록 생성 | +| lib/social-links.js | 사이트 설정 SNS 링크 아이콘 프리셋·사용자 SVG 정리·URL 자동 보정·레거시 JSON 문자열 복구·공개 노출 목록 생성 | ## Nuxt 모듈 diff --git a/docs/spec.md b/docs/spec.md index cca0314..35b0bba 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -38,7 +38,7 @@ - `Escape` 키는 통합 검색 모달이 열려 있으면 최우선으로 닫고, 그다음 사용자 드롭다운, 이어서 모바일에서만 좌측 슬라이드 메뉴를 닫는다. - `/` 키는 `INPUT`·`TEXTAREA`·`SELECT`·`contenteditable`에 포커스가 없고 `Ctrl`/`Meta`/`Alt`와 함께 눌리지 않을 때 통합 검색 모달을 연다. 헤더 검색 영역(`md+`) 클릭으로도 동일하게 연다. - 헤더 우측 사용자 아이콘 버튼은 로그인 상태면 회원 아바타/닉네임과 설정·로그아웃 메뉴를, 비로그인 상태면 Guest·Sign up·Sign in 메뉴를 표시한다. 회원 아바타 이미지가 없거나 비로그인 상태인 경우 사용자 메뉴 버튼에는 사람 아이콘을 표시한다. -- 오른쪽 사이드바의 FOLLOW 영역은 사이트 설정의 SNS 링크 목록을 기준으로 표시한다. 관리자가 아이콘 프리셋 또는 직접 SVG 아이콘과 주소를 등록한 항목만 노출하며, 등록된 항목이 없으면 FOLLOW 영역 자체를 숨긴다. SNS 주소는 `https://`를 생략해도 저장 시 자동 보정한다. +- 오른쪽 사이드바의 FOLLOW 영역은 사이트 설정의 SNS 링크 목록을 기준으로 표시한다. 관리자가 아이콘 프리셋 또는 직접 SVG 아이콘과 주소를 등록한 항목만 노출하며, 등록된 항목이 없으면 FOLLOW 영역 자체를 숨긴다. SNS 주소는 `https://`를 생략해도 저장 시 자동 보정한다. 관리자 편집 화면은 아이콘과 주소를 기본 입력으로 사용하며, 직접 SVG 프리셋을 선택했을 때만 SVG 코드 입력을 추가로 표시한다. ### 공개 화면 색상 diff --git a/docs/update.md b/docs/update.md index 7e8c9c4..7d066df 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,5 +1,11 @@ # 업데이트 이력 +## v1.5.41 + +- 관리자 사이트 설정: SNS 링크가 JSONB 배열이 아닌 JSON 문자열로 저장되어 저장 후 목록이 비어 보이던 문제 수정. +- 관리자 사이트 설정: 기존에 잘못 저장된 SNS 링크 JSON 문자열을 배열로 복구하는 DB 마이그레이션 추가. +- 관리자 사이트 설정: SNS 편집 UI에서 불필요한 이름 입력칸을 제거하고 아이콘·주소 중심 레이아웃으로 정리. + ## v1.5.40 - 관리자 사이트 설정: SNS 링크 주소 입력 시 `https://`를 생략해도 자동 보정되도록 수정. diff --git a/lib/social-links.js b/lib/social-links.js index a7dd63b..18ee647 100644 --- a/lib/social-links.js +++ b/lib/social-links.js @@ -126,6 +126,14 @@ export const normalizeSocialLinkItem = (item, index = 0) => { * @returns {Array<{ id: string, icon: string, label: string, url: string, iconSvg: string }>} 정리된 SNS 링크 목록 */ export const normalizeSocialLinks = (value) => { + if (typeof value === 'string') { + try { + return normalizeSocialLinks(JSON.parse(value)) + } catch { + return [] + } + } + const source = Array.isArray(value) ? value : Object.entries(value && typeof value === 'object' ? value : {}).map(([icon, url]) => ({ icon, url })) diff --git a/package-lock.json b/package-lock.json index 347ff09..03f7b21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "sori.studio", - "version": "1.5.40", + "version": "1.5.41", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "sori.studio", - "version": "1.5.40", + "version": "1.5.41", "hasInstallScript": true, "dependencies": { "@nuxtjs/tailwindcss": "^6.14.0", diff --git a/package.json b/package.json index 2c2346d..5250406 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sori.studio", - "version": "1.5.40", + "version": "1.5.41", "private": true, "type": "module", "imports": { diff --git a/pages/admin/settings/index.vue b/pages/admin/settings/index.vue index 60a3fc9..b60ab43 100644 --- a/pages/admin/settings/index.vue +++ b/pages/admin/settings/index.vue @@ -1273,12 +1273,8 @@ const removeSocialLink = (index) => { */ const updateSocialLinkIcon = (index, icon) => { const preset = getSocialIconPreset(icon) - const currentPreset = getSocialIconPreset(form.socialLinks[index].icon) form.socialLinks[index].icon = preset.icon - - if (!form.socialLinks[index].label || form.socialLinks[index].label === currentPreset.label) { - form.socialLinks[index].label = preset.label - } + form.socialLinks[index].label = preset.label if (preset.icon !== 'custom') { form.socialLinks[index].iconSvg = '' @@ -2185,7 +2181,7 @@ onBeforeUnmount(() => {
{
-