2026-08-10 ServiceOn Blog 작업일지입니다. 오늘은 1건의 작업을 진행했습니다.
포스팅 형식 이원화 (카드뉴스 / 매거진)
현상/기능
- 카드뉴스 모달의 “단독 페이지로 열기” 의미가 있는지 → 제거
현상/기능
- 블로그 포스팅 형식을 두 가지로: 현재 매거진형 유지 + 카드뉴스형 신규
- 카드뉴스는 RSS 뉴스 1건을 각각 별도 블로그 포스트로 저장
- 웹은 카드뉴스형이 기본, 목록 클릭 시 모달 팝업 + 좌/우로 이전·다음 뉴스 이동
- 매거진은 별도 메뉴로 추가
- 모듈 단위 분리(1), DB 수정(2), Gmail 수신 admin@serviceon.kr(3), 현재 화면=카드뉴스+매거진 메뉴(4)
작업
- 단독 페이지(`/posts/[id]`) 링크·`next/link` 제거, 원문 보기만 유지. Summary 갱신
- shared: `BlogPostFormat`, `POST_FORMATS`, `POST_FORMAT_LABELS`, `normalizePostFormat` 추가, `BlogPost`/`BlogPostUpsertInput` 에 `postFormat`·`sourceUrl`, `MOCK_POST_SEEDS` → 기본값 주입
- db: `PostFormatFilter` 타입, buildArgs 에 format/post_format/source_url 인자, `mapPost` 매핑(구 스키마 대비 fallback='magazine'), `mockCall` format 필터
- web API `/api/posts`: `format` 쿼리(card 기본 | magazine | all) → 3개 프로시저에 전달
- web 신규 컴포넌트
- 카드 1장 (커버·카테고리·시간·읽기시간, 클릭 시 모달 오픈)
- 카드 그리드 + 모달 index 컨트롤 (목록 갱신 시 모달 자동 닫힘)
- 상세 모달, 좌/우 버튼과 ←/→ 키로 이전·다음, ESC·backdrop 닫기, body 스크롤 락, 원문 링크
- 헤더 메뉴 (카드뉴스 `/` ↔ 매거진 `/magazine`)
- 두 페이지 공용 서버 셸 (SSR 초기 데이터 + format 주입)
- web 수정: (카드뉴스 기본), `magazine/` 신규(TimelineFeed 유지), `BlogHomeClient`(format prop 으로 그리드/타임라인 분기), `Header`(MainNav 삽입), `posts/[id]`(같은 형식 내 이전·다음, 원문 보기, 목록 링크 분기)
- admin: 포스트 편집에 `포스팅 형식` select + 카드뉴스일 때 `원문 기사 URL` 입력, 목록에 형식 표기, `api/posts` 에서 형식 정규화(매거진이면 sourceUrl 무시)
- n8n `t2` 워크플로우
- `Insert Blog Post`: `CALL 저장 절차('', $1..$7, 'magazine', NULL)` 로 파라미터 수 보정
- `Send a message`(Gmail): resource/operation/sendTo(admin@serviceon.kr)/자격증명 설정 후 `AI Agent` 출력에 연결, `onError=continueRegularOutput`(메일 실패가 발행을 막지 않도록)
- 카드뉴스 분기 신규: `Edit Fields` → `AI Agent - Card`(기사 1건당 1회) → `Prepare Card Post` → `Insert Card Post`(`post_format='card'`, `source_url`)
- 보조 노드: `OpenAI Chat Model - Card`, `Structured Output Parser - Card`(autoFix), `OpenAI Parser Fix Model - Card`, `Sticky Note - Card`
- 수동 실행 검증: 매거진 1건 + 카드 5건 저장 성공 (`postFormat`, `sourceUrl` 정상)
- 미해결(외부 설정): Google Cloud 프로젝트 150072012678 의 Gmail API 미활성화 → 메일 발송 403