프롬프트 대거 변경 진행(1차 : POC-SW 전문 기획자)
측근 POC를 통해서, AI 퀄리티가 현저하게 떨어지는 것을 확인하였음
Pain Point 1 : 일러스트/실사화 위주의 프롬프트 구성
- 고 퀄리티, 신기한 이미지를 위주로 생성하여 시각적 자극을 주어서 구매를 유도하자! 라는 것이 Point였는데, 실상 폰케이스를 구매하는 사람들은 '여성' 유저임, 대다수의 여성 유저들은 귀여운 것, 예쁜 것들이 제품/디자인을 고르는 기준이 됨
=> 실사화/일러스트 프롬프트 조건 삭제, 이미지 분위기 설정
Pain Point 2 : 키워드를 풍성하게 하는 것(비전공자 시점의 검증 필요-2차 POC 예정)
- 사용자가 기본 키워드를 던지면, 해당 내용을 풍성하게 해서 그 중에 고르게 하자!
하지만!
요즘 사용자들은 이미지 생성을 위해 자신의 생각과 Needs를 AI에게 전달하기 때문에 프롬프트에서 다듬어서 이미지를 생성하는 것은 유저의 needs에 저해되는 행동일 수 있음
=> 프롬프트 수정 진행(A/B 테스트 진행 필요함)
Pain Point 3 : 휴대폰 케이스 Back image라는 고정 관념
- 프롬프트에서 phone case back design 이라는 요소를 넣으니, 이미지 자체를 휴대폰 뒷면으로 출력해서, 렌즈가 겹치고, 기종이 다르게 나오는 등 퀄리티가 떨어지는 문제가 발생했음
-> 배경을 지우는 기능이 이미 들어가 있기 때문에, phone case back design 가 아닌 AI를 활용한 이미지 생성으로만 진행해도, 이미지를 사용이 가능한 상황임
=> 프롬프트 수정(비율 9:16)
Check Point 1. 누끼 비용 절감
- 이미지 생성 시 누끼를 따는 API를 타는데 AI를 타는거라서 비용이 들어감(1실행 시 비용 4배)
=> 사용자 실행 시 배경 제거 진행하도록 로직 수정


나중에 모델바꿔야겠다 ㅠㅠㅠ
'IT > blnk.co' 카테고리의 다른 글
| [1인 개발 - blnk.co] 방향성 문제 고민 (0) | 2026.05.23 |
|---|---|
| [1인 개발 - blnk.co] 2차 문제 직면.. (0) | 2026.05.23 |
| [1인 개발 - blnk.co] 1달차 - 4주만에 647커밋 (0) | 2026.05.20 |
블로그 꽃단장 파일 공유(w/claude)
skin-share.html 코드 가이드
— 파일 구조, 동작 원리, 커스터마이징 지점
tistory-skin-guide.md(제작 과정/이슈 해결기)와 함께 보는 코드 레퍼런스입니다.
이 문서는 "코드가 어떻게 생겼고, 어디를 고치면 무엇이 바뀌는가"에 집중합니다.
1. 파일 한 장 요약
skin-share.html 하나에 HTML + CSS + JS가 전부 들어있습니다. (약 1,300줄)
skin-share.html
├─ <head>
│ ├─ Google Fonts 로드 (Nanum Pen Script / Noto Serif KR / Noto Sans KR)
│ └─ <style> ······································ 약 10~590행
│ ├─ ① 디자인 토큰 (:root / body.dk)
│ ├─ ② 베이스 & 레이아웃 (헤더, 그리드, 사이드바)
│ ├─ ③ 글 카드 + 미리보기 클램프
│ ├─ ④ 페이지네이션
│ ├─ ⑤ 사이드바 위젯 (검색/카테고리/태그/RSS)
│ ├─ ⑥ Tistory 자동 삽입 요소 덮어쓰기 (!important 지대)
│ ├─ ⑦ 첨부파일 재스타일 + 확장자 칩
│ └─ ⑧ 상세 페이지 모드 (body.detail …)
├─ <body class="tt-body-category">
│ ├─ 헤더 (블로그명, 카테고리 내비, 다크모드 토글)
│ ├─ 메인 컬럼: <s_list> 글 목록 + <s_article_rep> 글 상세
│ │ └─ 카드 내부에 태그/명함/댓글 등 "상세 전용 요소" 동봉
│ ├─ 사이드바: 검색 / 카테고리 / 태그 클라우드 / RSS
│ └─ 푸터
└─ <script> ········································ 약 724~1330행
├─ (a) localStorage 캐시 정리
├─ (b) 첨부파일 DOM 재구성
├─ (c) 페이지네이션 재구성
├─ (d) 페이지 타입 감지 + 상세 모드 토글 ← 가장 큰 블록
│ ├─ 사이드바 카테고리 재구성
│ ├─ 글 태그 정리(멀티 태그 분리) + 폴백
│ ├─ author-box / atags 중복 제거
│ └─ 사이드바 태그 클라우드 정리
├─ (e) 다크모드 토글 + 복원
└─ (f) 스크롤 진행 바
2. 디자인 토큰 — 색/스타일을 바꾸려면 여기만
모든 색상은 CSS 변수로 선언되어 있고, 라이트/다크가 같은 변수명을 공유합니다.
변수만 바꾸면 전체 테마가 일괄 변경됩니다.
:root { /* 라이트 모드 */
--ac:#B07828; /* 액센트 (브론즈) — 링크 호버, 현재 페이지, 칩 */
--ac-a:rgba(176,120,40,.13); /* 액센트 연한 배경 */
--bg:#F8F6F3; --nav:#EFE9E2; /* 페이지 배경 / 내비 배경 */
--tx:#1A1814; --sub:#4A4238; --mt:#6E665E; /* 본문 / 보조 / 메타 텍스트 */
--bd:#E4DDD5; --tb:#EDE7DF; --cb:#FFFEFB; /* 보더 / 태그 배경 / 카드 배경 */
}
body.dk { /* 다크 모드 — 같은 변수를 어두운 값으로 재정의 */ }
| 바꾸고 싶은 것 | 수정 위치 |
|---|---|
| 포인트 컬러 | --ac, --ac-a (라이트/다크 각각) |
| 전체 배경 톤 | --bg, --cb, --bd |
| 본문 폰트 | body{font-family:...} + <head>의 Google Fonts 링크 |
| 로고 손글씨체 | Nanum Pen Script 사용처 (헤더 로고, 아바타 이니셜) |
규칙: 색을 하드코딩하지 말 것. 새 요소를 추가할 때도 var(--ac) 등 변수를 쓰면
다크모드가 공짜로 따라옵니다. (예외: 확장자 칩 색상은 파일 형식 고유색이라 하드코딩)
3. 한 장 두 모드: 목록 ↔ 상세
이 스킨의 핵심 트릭입니다. 카드(.card) 마크업은 하나인데,
body 클래스에 따라 두 가지 모습으로 렌더됩니다.
목록 모드 (기본) 상세 모드 (body.detail)
┌─ .card ──────────┐ .card → 보더/배경 제거, 아티클화
│ 제목 (.pt) │ .pt → 28px 대제목
│ 미리보기 2줄 (.pe) │ .pe → 클램프 해제, 전문 표시
│ 태그 (compact) │ .rep-extra → 표시 (명함, 댓글 등)
└──────────────────┘ + "← 목록으로" 링크 JS 삽입
동작 순서:
<body class="tt-body-category">→ Tistory가tt-body-page(상세) 등으로 치환- JS(d블록)가 클래스를 읽어
body.detail클래스를 추가/생략 - CSS의
body.detail ...섹션(⑧)이 카드를 아티클로 변환
// 정식 판별 우선, 변수 미처리 시 휴리스틱 폴백
if (/tt-body-/.test(bodyCls)) {
isDetail = /tt-body-page(\s|$)/.test(bodyCls);
} else {
isDetail = cards.length === 1;
}
상세 전용 요소 패턴: 명함·댓글처럼 상세에서만 보일 요소는 카드 안에class="rep-extra"로 넣어두고 기본 display:none, body.detail에서만 표시합니다.
새 상세 전용 요소를 추가할 때 이 클래스만 붙이면 됩니다.
4. 미리보기 클램프 — .pe의 방어 설계
목록의 본문 미리보기는 Tistory가 어떤 HTML이든 넣을 수 있는 영역입니다.
그래서 3중 방어를 합니다:
.pe{ max-height:53px !important; overflow:hidden !important;
display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; }
.pe img,.pe figure,.pe video,.pe iframe,.pe table{ display:none !important; } /* 미디어 제거 */
.pe *{ font-size:14px !important; margin:0 !important; ... } /* 평탄화 */
상세 모드에서는 이걸 전부 해제합니다 (body.detail .pe{max-height:none !important;...}).
미리보기 줄 수를 바꾸려면 -webkit-line-clamp 값과 max-height(줄수 × line-height)를 함께 수정하세요.
5. !important 지대 — Tistory 덮어쓰기 레이어
⑥~⑦ 섹션은 거의 모든 선언에 !important가 붙어 있습니다. 버그가 아니라 의도입니다.
Tistory가 런타임에 자체 CSS와 인라인 스타일을 주입하기 때문에, 이를 이기려면 필요합니다.
주요 덮어쓰기 대상:
| 대상 | 처리 |
|---|---|
| 공감/구독/프로필 위젯 | display:none (스킨 자체 디자인으로 대체) |
another_category (카테고리의 다른 글) |
테이블 → 이전 글/다음 글 2칸 카드 그리드로 변환. tr:first-child::before{content:'← 이전 글'} 같은 가상요소로 라벨링 |
| 첨부파일 블록 | 기본 아이콘 숨기고 확장자 칩 + DOWNLOAD 텍스트 링크로 재구성 |
| 본문 표 | border-collapse + 변수 기반 보더로 통일 |
| 댓글 React 앱 | [class*="tt_"] 셀렉터로 폰트/색만 덮어씀 (DOM은 건드리지 않음) |
여기를 수정할 때 주의: 셀렉터가 div[class*="another_category"]처럼 부분 일치인 이유는
Tistory가 클래스명을 바꾸는 경우가 있어서입니다. 정확한 클래스명에 의존하지 마세요.
6. JS 모듈별 가이드
전부 IIFE(즉시실행함수)로 독립되어 있어 하나가 죽어도 나머지는 동작합니다.
중요 로직은 try/catch로 감싸고, 실패 시 console.warn('[hyos] ...')을 남깁니다.
(b) 첨부파일 재구성
.tt_attachment등에서 파일명/크기/링크를 파싱- 확장자를 추출해
<span class="file-ext-ico" data-ext="pdf">칩 삽입 - 새 확장자 색을 추가하려면: CSS의
.file-ext-ico[data-ext="..."]규칙 한 줄 추가
(c) 페이지네이션 재구성
- 서버가 렌더한 번호 링크에서
현재 페이지(href 없는 항목),전체 페이지,URL 패턴을 수집 ‹ 1 … 4 5 6 … 23 ›형태로 다시 그림- 윈도우 크기 변경:
var win = 2(현재 ±2) 값 수정 - 변수 미치환(
[##_잔존) 시 페이지네이션 전체 숨김 — raw 텍스트 노출 방지
(d) 페이지 타입 감지 블록 (메인)
가장 큰 블록. 내부 순서가 중요합니다:
1. body_id로 상세/목록 판별 → body.detail 토글
2. 상세면: "← 목록으로" 링크 삽입
3. 사이드바 카테고리 재구성 ← try/catch (실패해도 원본 노출)
4. 글 태그 정리: '#' 멀티 태그 분리 → 칩 재구성
└ 폴백 사다리: s_tag_label 결과 → 본문 /tag/ 링크 → 페이지 JSON
→ 모바일 페이지(/m/...) fetch → 전부 실패 시 숨김
5. author-box / atags 중복 제거
6. 사이드바 태그 클라우드 정리 (멀티 태그 분리 동일 적용)
태그 폴백은 0ms / 800ms / 2500ms 3회 재시도합니다 — Tistory React가
비동기로 마운트되기 때문. 이 패턴은 React 영역을 읽는 모든 코드에 필요합니다.
(e) 다크모드
document.getElementById('b').classList.toggle('dk', dk); // className= 금지!
localStorage.setItem('hyos-dark', dk ? '1' : '0');
classList.toggle을 쓰는 이유:className =으로 덮어쓰면 Tistory의tt-body-page클래스가 날아가 레이아웃이 깨집니다 (실제 겪은 버그)- 새로고침 시 localStorage에서 복원
(f) 스크롤 진행 바
헤더 위 2px 바(#prog)의 너비를 스크롤 비율로 갱신. 필요 없으면#prog 요소와 마지막 scroll 리스너만 지우면 됩니다.
7. 공통 패턴 (새 기능 추가 시 따라할 것)
패턴 1 — 미치환 변수 가드
if (/\[##_/.test(el.innerHTML)) { /* 변수 미처리 → 폴백 or 숨김 */ }
모든 동적 영역의 첫 줄. raw 텍스트가 사용자에게 노출되는 것을 막습니다.
패턴 2 — 폴백 사다리
정식 치환자 → DOM 정규화 → 대체 소스(RSS, /m/ 페이지) → 깔끔하게 숨김
어느 단계에서 성공하든 결과 UI는 동일하게. 전부 실패하면 빈 껍데기 대신 섹션 자체를 숨깁니다.
패턴 3 — 비동기 재시도
if (!run()) {
setTimeout(function(){ run(); }, 800);
setTimeout(function(){ run(); }, 2500);
}
Tistory React 마운트를 기다리는 표준 방법. MutationObserver보다 단순하고 충분합니다.
패턴 4 — 네임스페이스
- CSS 클래스:
hyos-file,hyos-cmt처럼 접두사 사용 (Tistory 클래스와 충돌 방지) - 콘솔 로그:
console.log('[hyos] ...')— 필터링 가능 - localStorage 키:
hyos-dark,hyosTagCache_v1— 버전 붙여서 마이그레이션 대비
8. 커스터마이징 빠른 레시피
| 하고 싶은 것 | 방법 |
|---|---|
| 포인트 컬러 변경 | :root와 body.dk의 --ac, --ac-a 수정 |
| 미리보기 3줄로 | .pe의 -webkit-line-clamp:3 + max-height 재계산 |
| 사이드바 위젯 순서 변경 | <aside class="sb"> 안의 .ss 블록 순서 바꾸기 |
| 사이드바 위젯 제거 | 해당 .ss 블록 삭제 (JS는 요소 없으면 자동 skip) |
| 페이지네이션 윈도우 | JS의 var win = 2 수정 |
| 확장자 칩 색 추가 | .file-ext-ico[data-ext="확장자"]{background:...} 추가 |
| 다크모드 기본값 ON | (e) 복원 IIFE에서 localStorage 체크 앞에 dk=true;... 분기 추가 |
| 명함(author-box) 수정 | class="author-box" 블록 — 이름/소개/이니셜은 플레이스홀더 검색 |
| 댓글 영역 스타일 | #hyos-cmt 하위 CSS — DOM 구조는 Tistory React 소유, CSS만 건드릴 것 |
9. 수정 시 지켜야 할 것 (안 깨뜨리는 법)
<s_...>블록 구조를 옮기지 마세요. 치환자는 자기 블록 안에서만 동작합니다.
특히<s_tag_label>,<s_random_tags>(반복 단위 주의),<s_paging_rep>.는href="..."로 감싸면 안 됩니다. 속성 전체를 출력하는 변수입니다.- body 클래스는
classList로만 조작하세요. - React 마운트 포인트(
data-tistory-react-app) 내부 DOM을 JS로 수정하지 마세요.
리렌더 시 덮어써집니다. CSS 덮어쓰기만 허용. - JS 추가는 새 IIFE로. 기존 블록에 끼워 넣지 말고 독립 블록 + try/catch로 추가하면
실패해도 다른 기능이 살아 있습니다. - 수정 후에는 5종 페이지를 모두 확인: 홈 / 글 상세 / 카테고리 / 태그 검색 / 마지막 페이지(글 1개).
깃허브로 따로 관리를 하고있지 않아서 파일로 전달 드리니 참고바랍니다~
'IT' 카테고리의 다른 글
| 티스토리 블로그 꽃단장 가이드 및 이슈 노트(w/ Claude Design) (0) | 2026.06.12 |
|---|---|
| [Claude] Claude 엄청남, 엄청남, 엄청남, 평서문 (2) | 2026.04.30 |
티스토리 블로그 꽃단장 가이드 및 이슈 노트(w/ Claude Design)
Tistory 커스텀 스킨 제작 가이드
— 디자인 시안부터 실서비스 적용까지, 삽질 기록과 해결법
대상 독자: HTML/CSS/JS 기본기가 있는 1~3년차 개발자.
이 문서는 실제로 블로그(hyos.blog) 스킨을 디자인 시안부터 만들어 Tistory에 적용하면서
겪은 이슈와 해결 과정을 정리한 것입니다.
0. 전체 흐름 요약
1) HTML/CSS로 디자인 시안 제작 (홈 + 상세 2종)
2) 시안을 Tistory 스킨(skin.html) 구조로 변환
3) 치환자(템플릿 변수) 적용 → 업로드 → 깨지는 부분 디버깅 (대부분의 시간이 여기)
4) JS 폴백/정규화 레이어 추가로 마무리핵심 교훈을 먼저 말하면:
- Tistory 치환자는 문서대로 동작하지 않는 경우가 많다. 변수가 빈값으로 치환되거나, 아예 치환되지 않고
텍스트가 그대로 남기도 한다. - 그래서 최종 구조는 "치환자 우선 + JS 폴백"의 2중 구조가 됐다. 서버가 제대로 렌더하면 그대로 쓰고, 실패하면 JS가 DOM/RSS/모바일 페이지에서 데이터를 긁어와 채운다.
- Tistory가 주입하는 기본 스타일과 싸우려면
!important가 생각보다 자주 필요하다.
1. 스킨 파일 구조
Tistory 스킨은 최소 두 파일이다.
| 파일 | 역할 |
|---|---|
skin.html |
페이지 전체 구조. 치환자 포함 |
style.css |
스타일 (skin.html 안에 <style>로 넣어도 동작함) |
우리는 관리 편의상 skin.html 하나에 CSS/JS를 전부 인라인으로 넣었다.
업로드는 블로그 관리 → 꾸미기 → 스킨 편집 → html 편집에서 한다.
페이지 1장으로 모든 화면을 처리한다
Tistory는 홈/카테고리/태그/검색/글상세가 전부 같은 skin.html로 렌더링된다.<s_list>(목록)와 <s_article_rep>(글)을 같은 파일에 두고, 현재 페이지 타입에 따라
Tistory가 알아서 한쪽만 채워준다. 우리는 여기에 더해 JS로 상세/목록 레이아웃을 토글했다.
2. 치환자(템플릿 변수) 기초
치환자는 형태이고, 반복/조건 블록은 <s_블록명>...</s_블록명> 태그다.
<s_list> <!-- 목록 페이지에서만 렌더 -->
<s_list_rep> <!-- 글 개수만큼 반복 -->
<a href=""></a>
</s_list_rep>
</s_list>
중요한 규칙: 변수는 지정된 블록 안에서만 치환된다.
블록 밖에 변수를 쓰면 치환되지 않고 raw 텍스트가 그대로 노출된다. (아래 이슈 4 참고)
3. 겪은 이슈와 해결법
이슈 1 — 홈 목록에서 글 전문이 다 보임
증상: 홈 화면 카드에 미리보기 2줄이 아니라 본문 전체가 출력됨.
원인: 목록에서 본문 변수를 그대로 쓰면 전문이 들어온다.
해결: CSS line-clamp로 강제 제한. Tistory가 어떤 HTML을 넣어주든 2줄로 자른다.
.preview{
display:-webkit-box;
-webkit-line-clamp:2;
-webkit-box-orient:vertical;
overflow:hidden;
}
/* 본문 안의 어떤 태그가 와도 스타일 통일 */
.preview *{
font-size:14px !important;
margin:0 !important;
background:none !important;
}
포인트: 미리보기 안에는 <p>, <h2>, <figure> 등 무엇이든 들어올 수 있으므로* 셀렉터 + !important로 전부 평탄화해야 한다.
이슈 2 — 상세 페이지와 목록 페이지 구분
증상: "마지막 페이지에 글이 1개만 있으면 상세 페이지로 보임."
원인(우리 실수): 처음에 "카드가 1개면 상세"라는 휴리스틱으로 판별했다.
목록 페이지의 마지막 페이지에 글이 1개 남자 상세로 오인.
해결: Tistory 공식 변수 tt-body-category를 사용한다.
<body id="b" class="tt-body-category">
렌더되면 tt-body-index(홈), tt-body-page(글 상세), tt-body-category,tt-body-tag, tt-body-search 같은 클래스가 붙는다.
var bodyCls = document.body.className || '';
var isDetail;
if (/tt-body-/.test(bodyCls)) {
isDetail = /tt-body-page(\s|$)/.test(bodyCls); // 정식 판별
} else {
isDetail = cards.length === 1; // 변수 미처리 시 폴백
}
교훈: 휴리스틱을 쓰더라도 항상 "정식 방법 우선, 휴리스틱은 폴백"으로 겹쳐라.
이슈 3 — 다크모드 토글이 페이지 판별을 깨뜨림
증상: 다크모드를 켜면 상세 페이지 레이아웃이 풀림.
원인: 토글 코드가 body.className = 'dk'로 클래스를 통째로 덮어써서tt-body-page 클래스가 날아갔다.
해결: classList.toggle 사용.
// BAD
document.body.className = dk ? 'dk' : '';
// GOOD
document.body.classList.toggle('dk', dk);
사소해 보이지만 실제로 한참 헤맨 버그. 남의 플랫폼 위에서는 body/root 요소의
클래스를 절대 통째로 덮어쓰지 말 것.
이슈 4 — 태그가 절대 안 나옴 (최대 난관)
증상: 글 하단 태그 영역이 항상 빈값. , 등
어떤 변수를 써도 안 나옴.
원인이 3겹이었다:
블록 누락: 태그 변수는 반드시
<s_tag_label>블록 안에 있어야 치환된다.<s_tag_label> <div class="atags"></div> </s_tag_label>블록 없이 변수만 쓰면 영원히 빈값/raw 텍스트.
길이 필터에 걸림: JS 정리 로직에서 "50자 넘는 태그는 쓰레기값"으로 버렸는데,
실제로는 사용자가 태그 입력란에NLP #자연어처리 #프롬프트엔지니어링 #...처럼
#로 이어 적은 80자짜리 태그 1개가 등록되어 있었다. 통째로 필터에 걸려 0개가 됨.입력 방식 자체의 문제: Tistory 에디터 하단 태그 입력란에서는
태그 하나 입력 후 엔터로 구분해야 개별 태그가 된다.#로 이어 적으면 전부 한 덩어리 태그로 저장되고,/tag/검색도 덩어리째로만 잡힌다.
해결:
// '#'로 이어 적은 멀티 태그를 분리해서 개별 칩으로 표시
var add = function (t) {
t = (t || '').trim();
if (!t) return;
if (t.indexOf('#') > 0 || /^#.+#/.test(t)) {
t.split('#').forEach(function (p) {
p = p.trim();
if (p && !seen[p] && p.length <= 80) { seen[p] = 1; tagSet.push(p); }
});
return;
}
t = t.replace(/^#/, '');
if (!t || seen[t] || t.length > 80) return;
seen[t] = 1; tagSet.push(t);
};
교훈:
- 변수가 안 나오면 먼저 해당 변수의 필수 블록(
<s_...>)을 확인하라. - "데이터가 안 온다"고 단정하기 전에 내 필터 로직이 데이터를 버리고 있는지 의심하라.
- 디버깅할 땐 기존(잘 동작하는) 스킨의 렌더 결과 HTML을 직접 까보는 게 가장 빠르다.
개발자도구에서 동작하는 스킨의 태그 영역 HTML을 복사해 비교하면서 원인을 찾았다.
이슈 5 — 사이드바 태그 클라우드의 반복 단위
증상: <s_random_tags>로 감싼 TAGS 섹션이 뜨긴 뜨는데 구조가 깨지거나,
섹션 제목까지 태그 개수만큼 복제됨.
원인: <s_random_tags>는 반복 블록이다. 섹션 전체를 감싸면 섹션이 통째로 반복된다.
<!-- BAD: 섹션 전체가 태그 수만큼 반복됨 -->
<s_random_tags>
<div class="ss">
<h3>TAGS</h3>
<div class="tc"><a href=""></a></div>
</div>
</s_random_tags>
<!-- GOOD: 반복시킬 <a> 하나만 감싼다 -->
<div class="ss">
<h3>TAGS</h3>
<div class="tc">
<s_random_tags><a href=""></a></s_random_tags>
</div>
</div>
교훈: <s_..._rep> 류 블록을 만나면 "이 블록의 반복 단위가 무엇인가"를 먼저 정하라.
이슈 6 — 페이지네이션 링크가 href= 로 깨짐
증상: 페이지 번호를 누르면 https://blog.com/href= 같은 이상한 URL로 이동.
원인: 는 URL이 아니라 href="..." 속성 전체를 출력하는
변수다. href="[##_...]"처럼 감싸면 href="href=..."로 깨진다.
<!-- BAD -->
<a href=""></a>
<!-- GOOD -->
<a ></a>
교훈: 치환자마다 출력 형식(URL만 vs 속성 전체)이 다르다. 깨지면
렌더된 HTML을 개발자도구로 열어 변수가 뭘 뱉었는지 확인하는 게 정답.
이슈 7 — ‹ › 버튼이 없는 페이지로 이동
증상: 이전/다음 화살표를 누르면 "없는 페이지" 에러.
해결: 서버 변수에 의존하지 않고 JS로 페이지네이션을 재구성했다.
- 서버가 렌더한 번호 링크에서 현재 페이지(href 없는 번호), 전체 페이지 수, URL 패턴을 수집
‹ 1 … 4 5 6 … 23 ›형태로 다시 그림 (현재 ±2 윈도우 + 첫/끝 + 줄임표)- 첫 페이지의
‹, 마지막 페이지의›는pointer-events:none+ 흐림 처리
var win = 2, lo = Math.max(1, cur - win), hi = Math.min(total, cur + win);
var pages = [1];
if (lo > 2) pages.push('…');
for (var i = lo; i <= hi; i++) { if (i > 1 && i < total) pages.push(i); }
if (hi < total - 1) pages.push('…');
if (total > 1) pages.push(total);
이슈 8 — 댓글 수가 항상 0
증상: 목록 카드의 댓글 카운트 변수가 4종
(rp_cnt, comment_count, comment_cnt, reply_cnt) 전부 빈값/0.
해결: RSS 폴백. Tistory RSS(/rss)의 각 <item>에는 댓글 수가 들어있다.
fetch('/rss').then(r => r.text()).then(xml => {
var doc = new DOMParser().parseFromString(xml, 'text/xml');
doc.querySelectorAll('item').forEach(it => {
var link = it.querySelector('link').textContent;
var slash = it.getElementsByTagNameNS(
'http://purl.org/rss/1.0/modules/slash/', 'comments')[0];
var cnt = slash ? parseInt(slash.textContent, 10) : 0;
// link의 pathname으로 카드와 매칭해서 카운트 표시
});
});
같은 도메인이라 CORS 문제 없음. 단, RSS 공개 설정(블로그 관리 → 콘텐츠)이 켜져 있어야 하고
RSS에 노출되는 글 수 제한이 있다는 점은 감안할 것.
이슈 9 — 신형 댓글/프로필은 React 마운트 방식
증상: 댓글 영역이 아예 비어 있음. 직접 <form>을 그려도 동작 안 함.
원인: 최신 Tistory는 댓글·프로필 카드를 React 앱으로 클라이언트에서 마운트한다.
스킨은 마운트 포인트만 제공해야 한다.
<div data-tistory-react-app="Comment"></div>
이 div를 두면 Tistory 스크립트가 댓글 목록+입력창을 통째로 렌더해준다.
디자인 통일은 마운트된 DOM을 CSS로 덮는 방식으로 처리:
#hyos-cmt [class*="tt_"] { font-family:'Noto Sans KR',sans-serif !important; }
주의: React가 비동기로 마운트되므로, 관련 DOM을 읽는 JS는setTimeout 재시도(예: 0ms / 800ms / 2500ms)를 걸어야 한다.
이슈 10 — 첨부파일 영역 깨짐 + 확장자 아이콘
증상: <s_attachment> 영역이 Tistory 기본 스타일과 충돌해 깨짐.
해결:
- 파일명에서 확장자를 JS로 파싱해
data-ext속성 부여 - 확장자별 색상 칩(PDF=빨강, HWP=파랑, XLS=초록...)을 CSS로 표시
- Tistory가 background를 덮어써서 칩 색상에
!important필수였다
.file-ext-ico[data-ext="pdf"]{ background:#c0392b !important; color:#fff !important; }
.file-ext-ico[data-ext="hwp"]{ background:#2e6ab0 !important; color:#fff !important; }
이슈 11 — 본문 표(table)가 안 보임
증상: 에디터에서 만든 표가 본문에서 투명/무스타일로 렌더됨.
원인: 에디터는 <table data-ke-align="..."> 형태로 저장하는데, 스킨 CSS에
table 스타일이 전혀 없었고 리셋 CSS가 보더를 다 지워버렸다.
해결: 본문 영역(.pe) 한정으로 table 스타일을 명시.
.pe table{border-collapse:collapse;width:100%;margin:20px 0;}
.pe th,.pe td{border:1px solid var(--bd);padding:10px 14px;text-align:left;}
.pe thead th{background:var(--tb);font-weight:600;}
교훈: 본문에는 에디터가 생성하는 모든 마크업(표, 인용, 코드, 이미지 캡션...)이
들어올 수 있다. 스킨 만들 때 에디터로 온갖 요소를 넣은 테스트 글을 하나 만들어두면
디버깅이 훨씬 빨라진다.
4. 최종 아키텍처: "치환자 우선 + JS 폴백"
여러 이슈를 거치며 정착한 패턴. 모든 동적 데이터에 동일하게 적용했다.
┌─ 1순위: Tistory 치환자가 정상 렌더 → 그대로 사용
├─ 2순위: DOM에 남은 흔적(미처리 변수, 링크 등)을 JS로 정규화
├─ 3순위: 다른 소스에서 페치 (RSS, 모바일 페이지 /m/...)
└─ 실패: 해당 섹션을 깔끔하게 숨김 (빈 껍데기 노출 금지)체크 코드 패턴:
var raw = el.innerHTML || '';
if (/\[##_/.test(raw)) {
// 치환자가 처리되지 않음 → 폴백 또는 숨김
}
[##_ 문자열이 남아있으면 변수 미처리라는 뜻이다. 이 체크 하나로
"raw 변수 텍스트가 사용자에게 노출되는" 최악의 상황을 방지할 수 있다.
5. 디버깅 워크플로우 (시간 아끼는 법)
- 개발자도구 → Elements가 진실이다. 스킨 코드가 아니라 렌더된 결과를 봐라.
변수가 뭘로 치환됐는지, 빈값인지, raw로 남았는지 바로 보인다. console.log를 스킨 JS에 심어라. 우리는[hyos]접두사로 통일해서[hyos] 태그 추출 결과: []처럼 각 폴백 단계의 결과를 찍었다.- 잘 동작하는 다른 스킨과 비교하라. 같은 블로그에 기본 스킨을 잠깐 적용해보고
해당 영역의 렌더 HTML을 복사해두면, 내 스킨에서 뭐가 다른지 즉시 비교된다. - 테스트용 글을 만들어라. 표, 코드블록, 이미지, 첨부파일, 태그 여러 개,
댓글 있는 글 — 전부 들어간 글 하나면 회귀 테스트가 된다. - 스킨 저장 후 강력 새로고침(Ctrl+Shift+R). Tistory는 캐시가 꽤 끈질기다.
6. 치환자 빠른 참조 (이번에 실제로 쓴 것들)
| 치환자 / 블록 | 용도 | 함정 |
|---|---|---|
tt-body-category |
페이지 타입 (tt-body-page 등) |
body class에 넣고 classList로만 조작 |
<s_list> / <s_list_rep> |
글 목록 / 반복 | 반복 단위에 주의 |
<s_article_rep> |
글 상세 | |
<s_tag_label> + |
글 태그 | 블록 필수. 변수 단독으론 절대 안 나옴 |
<s_random_tags> + |
사이드바 태그 클라우드 | 반복 블록임 — <a> 하나만 감싸기 |
|
페이지 링크 | href= 포함 속성 전체 출력. href="..."로 감싸면 깨짐 |
<s_attachment> |
첨부파일 | Tistory 기본 스타일과 충돌 → !important |
data-tistory-react-app="Comment" |
댓글 (신형) | React 비동기 마운트. JS 재시도 필요 |
/rss |
댓글 수 폴백 | slash:comments 네임스페이스 파싱 |
/m/{글번호} |
모바일 렌더 페이지 | 태그 등 데이터 폴백 소스로 활용 가능 |
7. 마지막 조언
- 처음부터 Tistory 구조에 맞춰 디자인하지 마라. 순수 HTML 시안을 먼저 완성하고
(디자인 의사결정이 자유로움), 그 다음 스킨으로 이식하는 편이 결과물이 훨씬 좋다. - 이식은 한 섹션씩. 헤더 → 목록 → 상세 → 사이드바 → 댓글 순으로 하나씩 옮기고
매번 업로드해서 확인해라. 한 번에 다 옮기면 어디서 깨졌는지 못 찾는다. - 플랫폼과 싸우지 말고 폴백을 쌓아라. 치환자가 안 먹는 건 흔한 일이다.
"정식 방법 → 정규화 → 대체 소스 → 숨김"의 사다리를 만들면 어떤 상황에도 깨지지 않는다.
행운을 빕니다. 🛠
'IT' 카테고리의 다른 글
| 블로그 꽃단장 파일 공유(w/claude) (1) | 2026.06.12 |
|---|---|
| [Claude] Claude 엄청남, 엄청남, 엄청남, 평서문 (2) | 2026.04.30 |
[1인 개발 - blnk.co] 방향성 문제 고민
1. 퀄리티 문제
flux-2로 돌렸는데, 퀄리티가 맘에 안든다. 그래서 큰맘먹고 nano-2로 바꿨는데
퀄리티는 좋다.

근데 내가 생각한건 한화이글스의 수리를 모티브로한 디자인인데..... 맘에 들지 않아... 멋있긴한데... 맘에 안들어.....
2. 처음부터 본질적인 부분을 고민하기 시작




여러가지를 확인해본 결과
시장 자체는 죽어있는게 아님, 근데 내가 걱정하는건 다른 것과 차별점이 있는가?
최초 구매부터 재구매까지 이어질 수 있는가? 에 대한 시작-지속성의 문제였음
3. 설문조사를 위한 opensurvey 가입
- 설문조사 진행 필요함(가입만..)
너무 답답해서 밖에 산책하다가 해결책을 찾아냈음
★4. 개인의 맘에 드는 디자인이 중요함 ★
- 아무리 잘 만든 컨텐츠여도, 내가 맘에 들지 않는다면 구매하지 않음
--->> 그럼 너의 취향은????
==> 프롬프트에 개인의 취향을 포함시키는 방향으로 진행하면 개인화가 더 명확해질 것으로 생각됨
> 멋있는것, 귀여운것, 매니악한 것 등 취향이 다 다름 그걸 4가지 결과로 뽑아내기엔 너무 방대하다.
① 사전에 레퍼런스 목업을 준비해서, 취향에 드는 항목을 셀렉함,
② 해당 레퍼런스 자료에 대한 프롬프트 or 태그 값을 main service 프롬트에 적용
③ 개인화 된 result 값 반영(구매확률 ↑)
'IT > blnk.co' 카테고리의 다른 글
| 프롬프트 대거 변경 진행(1차 : POC-SW 전문 기획자) (0) | 2026.06.22 |
|---|---|
| [1인 개발 - blnk.co] 2차 문제 직면.. (0) | 2026.05.23 |
| [1인 개발 - blnk.co] 1달차 - 4주만에 647커밋 (0) | 2026.05.20 |
[1인 개발 - blnk.co] 2차 문제 직면..
#TODO 1번 : 이미지 합성 작업 진행
1. png 파일의 배경을 투명화를 시키는 작업을 진행함(근데 Back에 진행해야하는데 Front로 mockup을 연결해놨음-(?))
> 수정하니까 이미지랑 mockup이랑 붙어서 정상적으로 나옴

2. 이번엔 이미지 생성할 때 카메라랑 겹치는 이슈가 있음
> 프롬프트를 수정하기로함
3. 책지피티야 데이터 낋여와

4. 프롬프트에 카메라 영역 프롬프트로 전달해서 이미지 생성하도록 시킴


흠.................. 이게 맞나...................
------------------
배경 유무에 따라서 Needs차이도 있고, 퀄리티 차이가 좀 나는 것 같아서, 배경은 ON/OFF 하기로 했음
그래서 누끼를 따는 모델을 하나더 도입함

바로 프로토타입 따기 진행

그리고 바로 인프라 설정하고, 배포 진행했는데

이상한 애들이 나오는 거임, 그래서 확인해보니, Claude API 과부하로 프롬트 작성하는애가 동작을 안했음(한글 번역도 안됨)
그래서 fallback 모델(sonnet) 추가헀음
---------------
생각해보자
- 케이스 mockup (1차 생성 완료)
- mockup+AI 이미지 합성 완료
주변 사람 반응
- 카메라 모양 더 맞춰야함
- 이미지 퀄리티 올려야할 것 같음(좀더 화려한 내용이 필요하다했음)
잔여 작업(user)
- QC 내용기반으로 mockup이미지 변경, 퀄리티용 프롬프트 진행 필요
- 옵션(그립톡, 무선충전, 색상)에 대한 렌딩 처리
'IT > blnk.co' 카테고리의 다른 글
| 프롬프트 대거 변경 진행(1차 : POC-SW 전문 기획자) (0) | 2026.06.22 |
|---|---|
| [1인 개발 - blnk.co] 방향성 문제 고민 (0) | 2026.05.23 |
| [1인 개발 - blnk.co] 1달차 - 4주만에 647커밋 (0) | 2026.05.20 |
[1인 개발 - blnk.co] 1달차 - 4주만에 647커밋
회고용으로 작성하는 블로그글!

중간에 퇴사한 회사 프리일좀 도와주느라 정신없어서, 방향 잃고 좀 방황헀는데
정신차려보니까 엠생도 이런 엠생이 없어서 다시 정신차림! (ESTJ 특 쉬면 지루함)
AI가 말아주는 milestones

# 지금 서비스 올린지 3주정도 지났음(서비스 오픈x)
- User, Admin 페이지 구성했고, AI도 붙여놨음

# 인프라
Service 는 MS Azure로 했고, storage는 MS blob으로 했음
(왜 ms 임? 스파크랩 엑셀러레이팅 신청했는데, azure 지원해준다고해서 그냥 azure로함-연락주세요...)

- 언어는 Next.js, react로 구성했음
- Github에 배포하면 자동으로 azure에 배포됨
- ORM은 Prisma
- DB는 Neon postgre (Neon은 Serverless Postgres service임-무료임)
# AI
- 전반적으로는 Cluade 사용함(max, opus)
- 이미지 생성 AI는 Fal.ai를 사용하는데 여기서 모델을 선택할 수 있음(모델 학습 있던 것 같은데 못찾겠음)
> 처음에는 flux 1모델을 썼는데 퀄리티가 별로였음
> Flux-2-Pro도 있고 그 안에 text 2 image, image 2 image 로 할 수 있는 세부 모델들이 있음
※ Fal.ai를 선택한 이유? => 금액 저렴해야함, API 지원 가능, 이미지 생성 특화 모델이어야했음
※ 왜 flux로 하느냐? => 제일 저렴함(중요), nano-bna도 선택할 수 있는데 비쌈(flux는 $0.03인데 nano-bna는 $0.08임)
# Issue. 사용자한테 입력받아서 이미지 생성 AI한테 넘기니까 문제가 좀 있음
- 한글 지원 안됨( fal.ai가 API로 사용할때는 한글 안받아줌, Web에서 Playground 할 때는 됨)
- 퀄리티가 너무 떨어짐, 원하는 프레임 구성이 안나옴
# 그래서 저는 문제를 해결하고자! Prompt 노예를 늘렸습니다!
- Claude console에서 따로 API를 요청해서 사용할 수 있음


1. 한글 지원
- 클로드한테 한글 번역하라고 시킴 API로 한글 번역해서 return함

이런식으로 한글 번역하는 애를 따로 만들었음 그리고 규칙도 따로 설정함

2-1. 이미지 퀄리티 문제
- 1차 시도는 관리자가 기본 프롬트를 수정할 수 있도록 하는 방향으로 진행했음(AI 도입 이전)
유저 프롬트 입력 창이 있으니 AI 한테는
예)이미지를 생성해야하는데 $1(옵션 1)을 기반으로 $2(내용)을 바탕으로 $3(추가옵션)을 반영해서 생성하고 $4(사진)을 참조해서 4개이상의 값을 줘!(좀 더 상세하게 쓰긴했음 ㅎㅎ..)
라고 하니까 퀄리티가 똥임
- 모델 자체가 가지고있는 데이터가 적을뿐더러, 매번 유저가 요청하는 프롬트가 명확하지 않음
- 그래서 얘도 AI 시켰음


프롬1번 노예가 한글로 넘겨주면, 얘가 그걸 가공해서 쫙 풀어줌
2-2. 이미지 합성
- 원래는 AI로 이미지를 생성하려고 했는데, 학습 데이터가 부족한건지 프롬트 2노예로 돌려도 안나오길래
- 그냥 mockup으로 합성하는게 제일 깔끔하겠다 싶었음
- 근데 mockup 이미지를 어디서 구하냐!
> AI로 만들었음 (Claude.ai/design - stitch)

와이어프래임 짤라고 챗지랑 제미니만 써봤는데, 클로드 디자인이 html코드로도 떨궈줘서 쓰기 제일 좋았음(지금보니 이것도 반영 작업해야되네)
근데 얘도 좀 멍청해서, 제미니한테 이미지 생성에 필요한 상세한 정보를 md로 넘겨달라해서 던졌는데, 좀 못알아들음...
그래서 일단 대충 맛 만나는 이미지를 쓰기로함...(이미지 생성 AI 추천좀..)
3. 프롬프트 검수 진행


여기서 통과되면 최종적으로 이미지 생성 진행!
프롬트4는 이미지 품질 검증도 할 예정

----
#잔여 업무
- 이미지 합성 기능(MVP) 도 붙여야하고, 사진 업로드도 붙여야함
- azure가 DB 읽어오는 속도가 너무 느림.... 구조적인 문제일 것 같음... 나중에 리팩토링을 하던, 스케일업을 하던해야지...
- 관리자 페이지 리팩토링
'IT > blnk.co' 카테고리의 다른 글
| 프롬프트 대거 변경 진행(1차 : POC-SW 전문 기획자) (0) | 2026.06.22 |
|---|---|
| [1인 개발 - blnk.co] 방향성 문제 고민 (0) | 2026.05.23 |
| [1인 개발 - blnk.co] 2차 문제 직면.. (0) | 2026.05.23 |
[Claude] Claude 엄청남, 엄청남, 엄청남, 평서문
🚀 [AI 활용 백서] 챗GPT의 가장 강력한 라이벌, '클로드(Claude)' 완벽 가이드: 왜 다들 클로드로 넘어갈까?
요즘 주변에서 "진짜 제대로 된 결과물을 뽑으려면 클로드(Claude)를 써야 해"라는 말, 한 번쯤 들어보셨나요?
챗GPT가 AI 시대를 열었다면, 클로드(Claude)는 우리가 AI를 일상과 업무에 사용하는 방식을 한 차원 끌어올린 '숨은 강자'입니다. 미국 실리콘밸리의 앤스로픽(Anthropic)이라는 기업에서 개발한 이 AI는 현재 전 세계 수많은 헤비 유저들의 최애 툴로 자리 잡고 있습니다.
도대체 클로드가 뭐길래 이렇게 난리인지, 그리고 당장 내 삶에 어떻게 써먹을 수 있는지 가장 쉽고 직관적으로 정리해 드립니다.
🧠 1. 클로드(Claude), 도대체 뭐가 그렇게 특별할까?
AI가 다 거기서 거기 아니냐고요? 클로드를 한 번이라도 써본 사람들은 다음 3가지 압도적인 장점 때문에 다시 돌아가지 못한다고 말합니다.
① "기계 냄새가 안 난다" : 소름 돋게 자연스러운 글쓰기
기존 AI로 블로그 글이나 이메일을 쓰게 해보고, '아, 이건 딱 봐도 번역기 돌린 AI 말투네' 하고 실망하신 적 있으시죠? 클로드의 가장 큰 무기는 바로 '인간보다 더 인간적인 문장력'입니다. 미묘한 뉘앙스, 부드러운 어조, 센스 있는 비유까지 찰떡같이 소화합니다. 정중한 사과 이메일, 통통 튀는 인스타그램 마케팅 문구, 감성적인 에세이까지 어떤 글이든 기가 막히게 써냅니다.
② 책 한 권을 통째로 외우는 '압도적인 기억력'
클로드는 한 번에 읽고 기억할 수 있는 정보의 양(컨텍스트 윈도우)이 어마어마합니다. 수십 장짜리 PDF 논문, 두꺼운 회사 규정집, 혹은 긴 소설 텍스트를 통째로 업로드해도 맥락을 절대 놓치지 않습니다. "이 100페이지짜리 문서에서 30대 여성에게 해당하는 혜택만 3줄로 요약해 줘"라고 하면 단 1초 만에 정확한 답을 찾아냅니다.
③ 눈앞에서 결과물이 뚝딱! 마법 같은 '아티팩트(Artifacts)'
클로드의 하이라이트 기능입니다. AI에게 뭔가를 부탁하면 텍스트로만 답을 주는 게 아니라, 대화창 옆에 아예 완성된 결과물(UI, 표, 다이어그램 등)을 띄워줍니다. 예를 들어 "오늘 섭취한 칼로리를 계산할 수 있는 웹 화면을 짜줘" 혹은 "복잡한 역사를 연도별 타임라인 그래픽으로 그려줘"라고 하면 그 자리에서 눈에 보이는 결과물을 만들어냅니다.
🎯 2. 나에게 맞는 클로드 모델 고르기
클로드에 접속해 보면 여러 가지 모델이 있어서 당황하실 수 있습니다. 딱 이 기준만 기억하세요!
- ⚡ Haiku (하이크): 엄청 빠르고 가볍습니다. 단순한 요약, 빠른 번역, 가벼운 아이디어 스케치에 딱 맞습니다.
- 🌟 Sonnet (소네트): 가장 강력히 추천하는 메인 모델입니다. (현재 3.5 Sonnet). 속도와 지능의 밸런스가 완벽하며, 글쓰기, 코딩, 복잡한 문서 분석 등 99%의 작업에서 최고의 성능을 냅니다. 무조건 이걸 쓰시면 됩니다.
- 👑 Opus (오퍼스): 가장 무겁고 똑똑하지만 속도는 조금 느립니다. 매우 고차원적인 논리 문제나 고난도 추론이 필요할 때만 가끔 꺼내 쓰는 필살기입니다.
🛠️ 3. 지금 당장 클로드 시작하는 법
설치나 복잡한 세팅은 전혀 필요 없습니다. 웹 브라우저만 있으면 누구나 바로 1분 만에 시작할 수 있습니다.
- 웹사이트 접속: Claude.ai 에 접속합니다.
- 간편 가입: 구글(Google) 계정 등으로 10초 만에 가입을 완료합니다.
- 앱 다운로드 (선택): 클로드를 매일 쓰고 싶다면 화면 하단이나 설정에서 Mac/Windows용 데스크톱 앱, 혹은 스마트폰 앱을 다운로드하세요. 접근성이 훨씬 좋아집니다.
🏃♂️ 마무리하며: 일단 아무거나 던져보세요!
AI 도구는 눈으로 읽기만 해서는 절대 내 것이 되지 않습니다. 지금 바로 클로드를 켜고, 평소에 귀찮았던 일이나 궁금했던 것을 편하게 물어보세요.
"냉장고에 계란, 양파, 베이컨이 있는데 이걸로 15분 만에 만들 수 있는 자취생용 레시피를 인스타그램 감성으로 써줄래?"
아마 첫 대답을 보는 순간, 여러분도 클로드의 매력에 푹 빠지게 되실 겁니다. 지금 바로 클로드와 함께 똑똑한 하루를 시작해 보세요!
'IT' 카테고리의 다른 글
| 블로그 꽃단장 파일 공유(w/claude) (1) | 2026.06.12 |
|---|---|
| 티스토리 블로그 꽃단장 가이드 및 이슈 노트(w/ Claude Design) (0) | 2026.06.12 |
[SI] startup junior PM 살아남기(#4) (RFP 분석)
저번 글에서 공공 SI의 전체적인 순서를 훑어봤는데, 오늘은 그중에서도 PM의 '눈치'와 '꼼꼼함'이 가장 필요한 RFP 분석과 과업대비표에 대해 써보려 합니다.
#1 RFP(제안요청서) 분석: 행간을 읽어야 산다
사업자가 공고를 보고 "오, 이거 할만하겠는데?" 싶어서 RFP를 열면 수십, 수백 페이지짜리 문서가 반겨줍니다. 그냥 읽으면 소설책이지만, PM은 여기서 **'독소 조항'**을 찾아내야 합니다.
- 기술 점수 vs 가격 점수: 보통 9:1인데, 가끔 8:2인 곳도 있습니다. 우리 회사가 기술력으로 압살할 수 있는지, 아니면 단가 싸움을 해야 하는지 여기서 사이즈가 나옵니다.
- 투입 인력 제한: 공공은 인력 등급에 예민합니다. "특급 기술자 X명 상주 필수" 같은 조건이 있는데, 정작 회사에 보낼 사람이 없으면 시작부터 꼬이는 겁니다.
- 인프라 및 SW 스펙: RHEL 버전이나 특정 DB, 보안 장비 규격이 딱 정해져 있는 경우가 많습니다. 기존 시스템이랑 안 맞으면 나중에 '호환성 지옥'에 빠지니 미리 체크해야 합니다.
▶ 산출물 > RFP 분석서(내부용)
#2 과업대비표: PM의 방패이자 생존줄
RFP 분석의 정점은 과업대비표 작성입니다. 말 그대로 "기관이 요구한 거(RFP)를 우리가 이렇게 하겠다(제안서)"라고 일대일로 매칭해놓은 표입니다.
- 이거 왜 하나요?: 나중에 사업 끝나갈 때쯤 발주처에서 "이거 왜 안 되어 있어요?"라고 할 때, "과업대비표 보시면 이 항목은 협의 제외였습니다"라고 말할 수 있는 유일한 근거가 됩니다.
- 작성 팁: 요구사항 번호 하나하나에 우리 기능을 매핑합니다. 이때 애매한 것들은 '협의 필요'나 '상세 설계 시 확정' 같은 문구를 잘 써야 합니다. 안 그러면 나중에 다 우리 책임 됩니다.(강조 x100)
※ 제안 단계에서 점수 따려고 "다 됩니다!" 하고 과업대비표에 '수용' 박아버리면, 실제 수행 들어갔을 때 PM은 밤샘 확정입니다... 적절한 밀당이 필요합니다.
▶ 산출물 > 과업대비표
#3 실무자 입장에서 본 RFP의 현실
기관 담당자분들도 기존 RFP를 복붙해서 만드시는 경우가 많다 보니, 가끔 앞뒤가 안 맞거나 이미 단종된 기술을 요구하는 경우도 있습니다.
이럴 땐 당황하지 말고 질의 응답 시간을 활용하거나, 기술 협상 때 명확히 짚고 넘어가야 합니다. "나중에 알아서 해주겠지"라는 생각은 공공 SI판에서는 절대 통하지 않습니다. (내 몸은 내가 지킨다...)
오늘도 바쁜 현대사회에서 살아남는 PM 분들 파이팅입니다.
'IT > PM' 카테고리의 다른 글
| [SI] startup junior PM 살아남기(#3) (사업의 순서) (0) | 2026.02.21 |
|---|---|
| [SI] startup junior PM 살아남기(#2)(부제 : 말 다이어트, no horse) (0) | 2026.01.11 |
| [SI] startup junior PM 살아남기(#1) (부제 : pivot) (0) | 2025.12.24 |
[SI] startup junior PM 살아남기(#3) (사업의 순서)
[공공 SI] 사업의 시작부터 계약까지, 전체 프로세스 훑어보기
감리 시정 조치 수행하고, 프로젝트 마무리 하느라 정신이 없어서 블로그를 한 달 건너 뛰었습니다. (바쁘다 바빠 현대사회)
오늘은 공공 SI 사업이 실제로 어떻게 시작되고, 계약까지 흘러가는지 그 단계를 정리해 보려 합니다.
#0 ISP 사업 (정보화전략계획, 필수 X)
SI 사업을 시작하려면 **'무엇이 문제이고, 무엇을 만들 것인가?'**에 대한 밑그림이 필요합니다. 공공기관 담당자분들은 시스템에 대해 전문적으로 깊게 아시는 분들이 많지 않기 때문에, **ISP(Information Strategy Planning)**를 먼저 발주하여 전문가들의 설계와 자문을 받습니다.
- 수행 내용: AS-IS 시스템 분석, 사용자 요구사항 분석, 인프라 환경 분석 및 목표 시스템 설계 등
- ▶ 산출물: ISP 결과 보고서
#1 사업 발주 (기관)
기관은 ISP 결과를 기반으로 **제안요청서(RFP)**를 작성합니다.
- RFP 작성: 이미 나와 있는 샘플이 많아서, 담당자분들이 기관 특색에 맞는 항목만 수정해서 '복붙' 하기도 합니다. (많이 써보신 분들은 뚝딱 만드십니다.)
- 공고 확인: 조달청 **나라장터(G2B)**에 사전규격 → 본 공고 순으로 올라옵니다.
- 사업자 대응: RFP를 분석해 사업 적합성, Cost, 불합리한 조항 등을 판단합니다. 부당한 항목은 수정을 요청할 수도 있다는데... (실제로 하는 건 본 적 없지만 가능은 하다고 합니다.)
- ▶ 산출물: 제안요청서(RFP)
#2 제안 및 입찰 (사업자)
사업자로 참여하기로 결정했다면, 이제 문서 싸움의 시작입니다.
- 제안서 작성: 영업, PM, 기획, 개발 PL 등이 붙어서 **발표자료(PPT)**와 정성제안서를 씁니다.
- Tip: 이때 자료를 잘 만들어두면 나중에 사업수행계획서나 보고 자료로 재활용할 게 많습니다.
- 주의: 수주 욕심에 무리한 추가 제안을 넣으면? 나중에 수행팀이 죽어납니다. (강조 x10000) 적절한 범위 안에서 제안해야 합니다.
- 입찰 (가격 투찰): 예산 대비 몇 %로 수행할지 금액을 써냅니다.
- 보통 점수를 따기 위해 하한선인 80% 근처로 던지고 보기도 하지만, 진짜 실력 있는 회사는 기술 점수에서 압살하기 때문에 금액에 크게 연연하지 않기도 합니다.
- ▶ 산출물: 제안 발표자료, 정성제안서
#3 발표 평가 (심사)
작성한 제안서를 들고 평가위원들 앞에서 발표를 진행합니다.
- 자체 평가: 발주기관이 직접 진행. 투명성을 위해 외부 위원을 일부 섞어서 평가합니다.
- 조달 평가: 조달청 주관으로 진행. 주로 외부 전문가들이 위원으로 참여합니다.
#4 기술 협상 (기관 & 사업자)
우선협상대상자로 선정되면 이제 디테일한 조율에 들어갑니다.
- RFP와 제안서를 펼쳐놓고 수정하거나 추가할 내용을 논의합니다. 이 단계가 원활하게 끝나야 최종 계약으로 넘어갑니다.
- ▶ 산출물: 기술협상서
#5 계약 (최종)
기관의 행정 부서와 사업자 간의 행정 처리가 마무리되면 비로소 사업이 시작됩니다.
- ▶ 산출물: 보안서약서, 각종 각서 및 계약서류
이제 계약까지 했으니 남은 건... 문서의 늪이죠. 다음 글은 본격적인 SI 산출물에 대해서 써보겠습니다~!
'IT > PM' 카테고리의 다른 글
| [SI] startup junior PM 살아남기(#4) (RFP 분석) (2) | 2026.04.05 |
|---|---|
| [SI] startup junior PM 살아남기(#2)(부제 : 말 다이어트, no horse) (0) | 2026.01.11 |
| [SI] startup junior PM 살아남기(#1) (부제 : pivot) (0) | 2025.12.24 |
[SI] startup junior PM 살아남기(#2)(부제 : 말 다이어트, no horse)
아주 정신 없는 한 주 였습니다. 프로젝트 말기라서 배포, QA, 산출물 정리, 감리 의 연속이라 이번 주 블로그 쓰는걸 까먹을 뻔 했지 뭡니까.(12월에 끝나야하는 사업인데 안끝나서 너무 힘들어요. 제발 좀 끝내자)
오늘은 목적성(방향성)에 대해 이야기를 해보고자 합니다.
#1 효율적인 시간 활용을 위한 잡음 제거
우리는 많은 회의를 진행합니다. 발주기관 주간회의, 사내 주간회의, 긴급회의, 중간회의, 개발팀 회의 등등...
지금 현황에서 남은 건 무엇인지, 놓치는 건 뭐가 있는지, 오늘은 무슨 일을 할지, 이슈가 무엇인지, 이슈를 처리하기 위해선 뭘 해야할지, 이슈에 대한 개개인의 의견이 뭐가 있는지, 오늘 점심은 뭘 먹을지, 언제 프로젝트가 끝날지 이 내용이 쓸모없는 건 아닐지 등
말을 장황하게 썼지만 전달하고자 하는 말은
목표와 결과(정량적)를 도출하기 위해 쓸모없는 말을 줄이자 입니다.
사람이 많아지면 말이 많아지고, 말이 많아지면 시간이 지체됩니다.
회의 때문에 시간이 지체되는건 정말 불필요한 행위라고 생각합니다.
회의가 쓸모없는 내용이 많다고 생각이 들면 참석자를 줄이던, 쓸모 없는 말을 줄이던 결정이 필요하다고 생각합니다.
#2 빠르게 회의를 끝내기 위한 목표 설정
회의를 할 땐 여러 포지션이 함께합니다. (관리, 기획, 개발)
사업관리는 보통 많은걸 알아야 하기 때문에 많은 이야기를 들으면 좋지만
기획, 개발은 사업관리의 상세한 내용을 알 필요가 없고
사업관리, 기획도 개발의 상세한 과정까지 들을 필요는 없습니다.
사람이 말을 하다보면, 내 얘기도 하고 싶고, 아는 얘기를 나누고 싶고, 이런말 저런말 하고 싶지만
우리는 시간이 없습니다. 할 내용만 빨리 회의하고 나가서 다른 일을 해야합니다.
보통 3가지만 잡고 들어가면 빠르게 끝납니다. ▲기간 ,▲목표, ▲정량적 수치
▲언제까지, ▲무엇을 구현해야하는데, ▲몇 프로까지 구현할 수 있는가
그 외에 이슈는 필요하면 바로바로 이야기하거나, 팀 내부에서 회의하시고 요청할 것 있으면 요청해주시기 바랍니다.!
라고 하면 보통 빨리 끝납니다.
위에 보고할 때도 마찬가지로 정량적 결과(구현 90%완료, 1월 12일까지 완료 목표 예정)만 보고 하면 됩니다.
말 많은거 싫어잉..
#3 Remind.bat
프로젝트 진행을 위해, 자료와 의견들을 전달합니다. 그리고 얼마 뒤에 개발팀에 물어보면 못들었다고 합니다.
??? 저번에 얘기했는데 왜 못들었어요?
사실 확인을 위해 근원지를 찾아가 봅니다. 아뿔싸.... 중간관리자가 바빠서 내용 전달을 못했습니다...
개발자가 논건 아니지만 우선순위가 해당 사항이 높기 때문에 전체적인 일정에 문제가 생겼습니다..
어쩌지..어쩌지... 뭘 어째요 일단 자료 전달주고 급한거라 빨리 좀 부탁한다고 요청드릴 것 밖에 할 수 없습니다..
정상적인 조직이라면, RnR이 명확하게 되어있고, 본인의 할 일만 해서 굴러가는 조직이라면 문제 될 일이 없습니다.
하지만 한 사람에게 너무 많은 일이 배정되어있다면, 놓치기가 쉽상입니다. 이는 조직 전체에 약영향을 미칩니다.
우리는 여기서 뻐꾸기가 되어야합니다.
이거 됐나요? 어떻게 진행되고 있나요? 뭐 필요한거 없나요?
지속적으로 체크해줘야 나중에 일어날 사고를 막을 수 있습니다.
그리고 우리 또한 이슈가 들어오면 어떻게 해야되지... 하고 붙자고 있지말고.. 던지십쇼... 일 잡고 있으면 나중에 큰일납니다...
다들 감기 조심하세요~
'IT > PM' 카테고리의 다른 글
| [SI] startup junior PM 살아남기(#4) (RFP 분석) (2) | 2026.04.05 |
|---|---|
| [SI] startup junior PM 살아남기(#3) (사업의 순서) (0) | 2026.02.21 |
| [SI] startup junior PM 살아남기(#1) (부제 : pivot) (0) | 2025.12.24 |