www.openads.co.kr
내용
- 제품개발 아이디어 검증부터 제품기획, 시제품제작, 양산, 출시까지 5단계 프로세스를 정리했습니다. 초기 브랜드가 자주 하는 실수와 현실적인 타임라인까지 확인해 보세요.
자기 브랜드 상품을 직접 만들어 파는 셀러가 빠르게 늘고 있어요. 외주 제작 인프라가 잘 갖춰져 있어서 아이디어만 있으면 누구든 도전할 수 있는 환경이 된 거죠.
그런데 실무로 들어가면 이야기가 달라져요. 어디서부터 손을 대야 할지 감이 안 잡히는 경우가 대부분이에요. 제품개발에는 일정한 단계가 있고, 그 흐름을 먼저 파악해두면 불필요한 시행착오를 줄일 수 있어요.
아이디어 구상부터 실제 판매 개시까지, 초기 브랜드가 알아두면 좋을 제품개발프로세스를 다섯 단계로 정리해봤어요.
검증 없는 아이디어는 재고로 돌아온다
신제품개발의 시작은 기획이 아니라 검증이에요. 시장에 수요가 있는지를 데이터로 먼저 확인하지 않으면, 결국 팔리지 않는 물건만 창고에 쌓이게 돼요.
검증 방법 자체는 복잡하지 않아요. 네이버 데이터랩이나 구글 트렌드에서 관련 키워드 검색량 흐름을 보고, 쿠팡이나 스마트스토어에서 유사 상품의 리뷰 수와 판매 추이를 살펴보면 시장 규모와 경쟁 강도를 가늠할 수 있어요.
여기서 놓치기 쉬운 포인트가 하나 있어요. 경쟁 제품의 별점 1점 리뷰를 분석하는 거예요. 소비자가 느끼는 불편, 기존 제품이 해결하지 못한 부분이 거기에 그대로 나와 있거든요. 이걸 파고들면 신제품의 차별화 방향이 잡혀요.
제품기획 단계에서 차별점을 먼저 설계해야 하는 이유
시장 확인이 끝나면 상품개발 기획으로 넘어가요. 이 단계에서 빠지기 쉬운 함정이 있는데, 어떤 물건을 만들지에만 집중하는 거예요. 중요한 건 경쟁 제품 대비 어떤 차이를 줄 수 있느냐예요. 차별점이 없는 제품은 결국 가격으로만 경쟁하게 되고, 마진이 빠르게 무너져요.
초기 브랜드가 실무에서 자주 활용하는 차별화 방향은 네 가지 정도로 나뉘어요. 디자인에 브랜드 감성을 입히는 방식, 리뷰 불만에서 나온 문제를 스펙으로 해결하는 기능 개선, 원료 산지나 개발 철학을 내세우는 스토리텔링, 세트 구성으로 체감 가치를 높이는 번들링이에요.
이 시점에 간단한 콘셉트 문서를 만들어두면 이후 공장 소통에서 커뮤니케이션 비용이 확 줄어요. 타깃 고객, 핵심 차별점, 희망 판매가, 원가 범위 정도만 한 장으로 정리하면 충분해요.
시제품 제작, 샘플 비교 없이 양산으로 넘어가면 안 되는 이유
기획 정리가 끝나면 실물 제작에 들어가요. 일반적인 흐름은 레퍼런스 자료를 공장에 전달하고, 1차 샘플을 받아 스펙과 마감을 확인한 뒤, 피드백을 반영해 2~3차 수정을 거쳐 최종 골드 샘플을 확정하는 순서예요.
실무에서 자주 보이는 실수가 있어요. 첫 샘플이 만족스럽다고 곧바로 양산을 진행하는 건데, 반드시 2~3곳 이상 공장에서 샘플을 받아 비교해야 해요. 같은 조건을 전달해도 공장마다 결과물 품질이 상당히 달라요.
샘플을 검토할 때는 시안과 실물의 색상 오차, 소재의 촉감과 내구성, 포장 상태에서의 배송 파손 위험, 해당 카테고리 필수 인증 여부(KC인증, 식약처 등)를 빠짐없이 체크해야 해요.
양산에서 초도 물량 조절이 중요한 진짜 이유
골드 샘플 확정 후 본격 제품양산에 들어가면, 가장 주의할 부분은 초도 수량이에요. 아직 시장 반응이 검증되지 않은 상태에서 대량 생산을 걸면 현금 흐름이 재고에 묶여요.
현실적인 양산 전략은 단계적으로 가는 거예요. 1차에서는 최소 MOQ로 시장 반응을 확인하고, 2차에서는 판매 데이터에 따라 수량을 조정해요. 안정적인 판매 흐름이 확인되면 그때 물량을 늘려서 단가 협상에 들어가는 게 안전해요.
양산 중에도 품질 관리는 멈추면 안 돼요. 생산 라인 초반 제품을 반드시 직접 확인하고, 불량률 허용 범위는 사전에 계약으로 잡아두는 게 리스크를 줄이는 방법이에요.
출시 전 세팅을 빠뜨리면 제품이 좋아도 안 팔린다
제품이 완성된 것과 판매가 가능한 것은 다른 문제예요. 출시 전에 갖춰야 할 요소가 꽤 있어요.
상표권 출원은 제조에 들어가기 전에 먼저 진행하는 게 안전하고, 상세페이지 기획과 제작, 제품 촬영, 판매 채널 입점(스마트스토어, 쿠팡, 자사몰 등), 초기 마케팅 전략(SNS, 인플루언서, 체험단 등)까지 세팅이 끝나야 실제 판매를 시작할 수 있어요.
특히 상세페이지는 소비자가 제품을 처음 만나는 접점이라 기획에 충분한 시간을 들여야 하고요. 마케팅은 출시 당일이 아니라 2~4주 전부터 사전 노출을 쌓아가는 게 초기 트래픽 확보에 효과적이에요.
전체 소요 기간
아이디어 검증에 1~2주, 제품기획에 1~2주, 시제품 제작에 4~8주, 양산에 2~6주, 출시 준비에 2~3주 정도가 걸려요. 카테고리와 제조 방식에 따라 편차는 있지만, 전체적으로 약 3~5개월이 일반적인 타임라인이에요.
제품개발 전체 흐름을 잡아두면 각 단계에서 어디에 리소스를 집중해야 할지가 분명해져요. 그런데 이 프로세스를 실행에 옮기려면 한 가지 더 결정할 게 있어요. 제조 방식에 따라 비용 구조와 일정, 품질 컨트롤 범위가 완전히 달라지거든요. OEM과 ODM 방식의 차이와 초기 브랜드에 맞는 선택 기준은 다음 글에서 이어서 풀어볼게요.
추천 콘텐츠
관련 콘텐츠가 없습니다.
원문 자막 펼치기
<!DOCTYPE html>
<!-- JSTL -->
<!-- SPRING -->
<!-- 커스텀태그 -->
<html lang="ko" >
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<!-- 반응형 viewport 추가 -->
<meta name="format-detection" content="telephone=no">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
<!-- 네이버 사이트 소유인증 -->
<meta name="naver-site-verification" content="4244cad2537c4ad9e1a7c07927574b123d4f26e4" />
<meta name='robots' content='index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1' />
<meta property="og:image" id="ogimg" content="https://openads-real.s3.amazonaws.com/openadsAdmin/images/contsThumb/자체 브랜드 제품개발 실무에서 바로 쓰는 5단계 프로세스 정리.png"/>
<meta name="twitter:image" content="https://openads-real.s3.amazonaws.com/openadsAdmin/images/contsThumb/자체 브랜드 제품개발 실무에서 바로 쓰는 5단계 프로세스 정리.png">
<meta name="thumbnail" content="https://openads-real.s3.amazonaws.com/openadsAdmin/images/contsThumb/자체 브랜드 제품개발 실무에서 바로 쓰는 5단계 프로세스 정리.png" />
<meta name="title" content="자체 브랜드 제품개발, 실무에서 바로 쓰는 5단계 프로세스 정리">
<meta name="description" content="자기 브랜드 상품을 직접 만들어 파는 셀러가 빠르게 늘고 있어요. 외주 제작 인프라가 잘 갖춰져 있어서 아이디어만 있으면 누구든 도전할 수 있는 환경이 된 거죠.
그런데 실">
<meta property="fb:app_id" content="769320239836476">
<meta property="og:url" content="https://www.openads.co.kr/content/contentDetail?contsId=19105">
<meta property="og:type" content="website">
<meta property="og:title" content="자체 브랜드 제품개발, 실무에서 바로 쓰는 5단계 프로세스 정리">
<meta property="og:image" content="https://openads-real.s3.amazonaws.com/openadsAdmin/images/contsThumb/자체 브랜드 제품개발 실무에서 바로 쓰는 5단계 프로세스 정리.png" />
<meta property="og:description" content="자기 브랜드 상품을 직접 만들어 파는 셀러가 빠르게 늘고 있어요. 외주 제작 인프라가 잘 갖춰져 있어서 아이디어만 있으면 누구든 도전할 수 있는 환경이 된 거죠.
그런데 실">
<meta name="twitter:site" value="@openads">
<meta name="twitter:creator" content="@openads">
<meta name="twitter:title" content="자체 브랜드 제품개발, 실무에서 바로 쓰는 5단계 프로세스 정리">
<meta name="twitter:image" content="https://openads-real.s3.amazonaws.com/openadsAdmin/images/contsThumb/자체 브랜드 제품개발 실무에서 바로 쓰는 5단계 프로세스 정리.png" />
<meta name="twitter:description" content="자기 브랜드 상품을 직접 만들어 파는 셀러가 빠르게 늘고 있어요. 외주 제작 인프라가 잘 갖춰져 있어서 아이디어만 있으면 누구든 도전할 수 있는 환경이 된 거죠.
그런데 실">
<meta name="twitter:card" content="summary_large_image">
<!-- MORE META TAG -->
<meta property="article:section" content="쇼핑몰 창업/운영">
<meta property="more:c_no" content="19105"> <!-- 콘텐츠 ID (*필수) 기사를 식별가능한 ID -->
<meta property="more:c_writer" content="원포인트(롸잇)"> <!-- 콘텐츠 작성자 (*필수) -->
<meta name="more_page_type" content="detail">
<link rel="canonical" href="https://www.openads.co.kr/content/contentDetail?contsId=19105">
<title>자체 브랜드 제품개발, 실무에서 바로 쓰는 5단계 프로세스 정리</title>
<link rel="shortcut icon" href="https://openads.co.kr/openads/images/openAdsFb.png" type="image/x-icon" >
<link rel="apple-touch-icon" href="https://openads.co.kr/openads/images/openAdsFb_white.png">
<!-- 퍼블리싱 페이지 기준으로 스크립트 추가. cdn의 경우 필요 시 다운로드 하여 사용 -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="/plugins/bootstrap-5.3.3/js/bootstrap.bundle.min.js"></script>
<script src="/plugins/swiper-5.3.8/swiper.min.js"></script>
<script src="/plugins/use-bootstrap-select-2.2.1/dist/use-bootstrap-select.min.js"></script>
<script src="/plugins/jquery-easyui-1.5.2/jquery.easyui.min.js"></script>
<script src="/plugins/jquery-easyui-1.5.2/datagrid-detailview.js"></script>
<script src="/plugins/jquery-easyui-1.5.2/datagrid-filter.js"></script>
<script src="/plugins/jquery-easyui-1.5.2/datagrid-bufferview.js"></script>
<script src="/plugins/jquery-easyui-1.5.2/datagrid-export.js"></script>
<script src="/plugins/filepond-4.28.2/filepond.js"></script>
<script src="/plugins/filepond-plugin-file-poster/filepond-plugin-file-poster.js"></script>
<script src="/plugins/FilePondPluginImagePreview-4.6.12/filepond-plugin-image-preview.js"></script>
<script src="/plugins/filepond-plugin-file-validate-type/filepond-plugin-file-validate-type.js"></script>
<script src="/plugins/filepond-plugin-file-validate-size/filepond-plugin-file-validate-size.js"></script>
<script src="/plugins/filepond-plugin-image-validate-size/filepond-plugin-image-validate-size.js"></script>
<script src="/plugins/filepond-plugin-image-resize/filepond-plugin-image-resize.js"></script>
<script src="/plugins/flatpickr-4.6.13/flatpickr.js"></script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr/dist/l10n/ko.js"></script>
<link rel="stylesheet" href="/plugins/filepond-4.28.2/filepond.css">
<link rel="stylesheet" href="/plugins/FilePondPluginImagePreview-4.6.12/filepond-plugin-image-preview.css">
<link rel="stylesheet" href="/plugins/filepond-plugin-file-poster/filepond-plugin-file-poster.css">
<link rel="stylesheet" href="/plugins/flatpickr-4.6.13/flatpickr.min.css">
<!-- FlipClock.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/flipclock/0.7.8/flipclock.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flipclock/0.7.8/flipclock.min.css">
<!-- 보류 -->
<script src="/plugins/jquery-ui-custom-1.12.1/jquery-ui.min.js"></script>
<!-- 기존 스크립트 -->
<script src="/plugins/jquery-upload-file-4.0.10/jquery.uploadfile.min.js"></script>
<script src="/plugins/select2-4.0.3/select2.full.min.js"></script>
<script src="/plugins/alertify-1.12.0/alertify.min.js"></script>
<script src="/plugins/jquery-scrollbar-gh-pages-0.2.10/jquery.scrollbar.min.js"></script>
<script src="/plugins/taggingJS-1.3.3/tagging.custom-1.1.js"></script>
<script src="/plugins/moment-2.18.1/moment.min.js"></script>
<script src="/plugins/daterangepicker-3.0.3/daterangepicker-custom.js"></script>
<script src="/plugins/daterangepicker-3.0.3/daterangepicker.js"></script> <!---->
<script src="/openads/script/config.js"></script>
<script async charset="utf-8" src="//static.dable.io/dist/plugin.min.js"></script>
<!-- 오픈애즈에서 자주 사용하는 공통 유틸성 모음. -->
<script src="/epe/openadsUtil.js"></script>
<script src="/epe/openadsFlareLane.js"></script>
<script src="/epe/openadsMessage.js"></script>
<script src="/epe/openadsBenefitUtil.js"></script>
<script src="/plugins/etc/format.20110630-1100.min.js"></script>
<script src="/epe/easyui.js"></script>
<script src="/epe/easyui-formatter.js"></script>
<script src="/epe/input-custom.js"></script>
<script src="/epe/jquery.js"></script>
<script src="/epe/jquery-form.js"></script>
<script src="/epe/jquery-extend.js"></script>
<script src="/epe/prototype-array.js"></script>
<script src="/epe/prototype-date.js"></script>
<script src="/epe/prototype-number.js"></script>
<script src="/epe/prototype-string.js"></script>
<script src="/epe/util.js"></script>
<script src="/epe/util-form.js"></script>
<script src="/epe/input-custom.js"></script> <!---->
<script src="/epe/scrollHistory.js"></script>
<!--스마트 에디터-->
<script src="/plugins/smarteditor2-2.8.2.3/js/HuskyEZCreator.js"></script>
<!-- 오픈애즈 데이터그리드 페이지네이션용 -->
<script src="/epe/pagination2.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.14.3/xlsx.full.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js"></script>
<!-- dayJS -->
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js"></script>
<!-- FlareLane -->
<script src="https://cdn.flarelane.com/WebSDK.js" charset="UTF-8"></script>
<script>
if (typeof FlareLane !== 'undefined') {
const flarelaneProjectId = "78caca4c-92a6-4b64-8e2d-284453260abb";
FlareLane.initialize({
projectId: flarelaneProjectId,
serviceWorkerPath: "/sw.js"
});
ou.flSetUserId(null);
// FlareLane: 방문 이벤트 + 태그 (모바일 구분 포함, 인앱 노출 조건은 대시보드에서 관리)
(function() {
const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
// 최근 7일 방문 일수 계산 (localStorage 기반)
const weeklyVisitCount = (function() {
let count = 0;
for (let i = 0; i < 7; i++) {
const d = new Date();
d.setDate(d.getDate() - i);
if (localStorage.getItem('fl_visit_' + d.toISOString().split('T')[0])) count++;
}
return count;
})();
// 오늘 방문 기록 저장
localStorage.setItem('fl_visit_' + today, '1');
// 세션 내 최초 1회만 visit 이벤트 발송
if (!sessionStorage.getItem('fl_visited')) {
sessionStorage.setItem('fl_visited', '1');
ou.fl.logEvent(ou.fl.keys.event.visit, {
visited_at: today,
host: window.location.hostname
});
}
// 매 세션 시작 시 태그 업데이트
ou.fl.setTags(ou.fl.buildVisitTags(today, weeklyVisitCount));
})();
// FlareLane: UTM 파라미터 태그 저장 (세션당 1회, 유입 경로 추적용)
(function() {
if (sessionStorage.getItem('fl_utm_tagged')) return;
var params = new URLSearchParams(window.location.search);
var utmSource = params.get('utm_source');
var medium = params.get('utm_medium');
var campaign = params.get('utm_campaign');
var gclid = params.get('gclid');
if (!utmSource && !gclid) return;
sessionStorage.setItem('fl_utm_tagged', '1');
var tags = {};
if (utmSource) {
tags.utm_source = utmSource;
sessionStorage.setItem('fl_utm_source', utmSource);
}
if (medium) {
tags.utm_medium = medium;
sessionStorage.setItem('fl_utm_medium', medium);
}
if (campaign) {
tags.utm_campaign = campaign;
sessionStorage.setItem('fl_utm_campaign', campaign);
}
if (gclid) {
tags.gclid = gclid;
sessionStorage.setItem('fl_gclid', gclid);
}
ou.fl.setTags(tags);
})();
}
</script>
<!-- 20221206 chart js added-->
<script src="/plugins/Chart.js/Chart.js"></script>
<script src="/plugins/handlebars-4.0.10/handlebars-v4.0.10.js"></script>
<script src="/plugins/handlebars-4.0.10/h.min.js"></script>
<!-- 20221220 photoSwipe added -->
<script src="/plugins/photoswipe/photoswipe.min.js"></script>
<script src="/plugins/photoswipe/photoswipe-ui-default.min.js"></script>
<!-- 20221221 serializeObject added -->
<script src="/plugins/jquery-serializeObject-2.0.3/jquery.serializeObject.min.js"></script>
<!-- dable widget -->
<script>
(function(d,a,b,l,e,_) {
d[b]=d[b]||function(){(d[b].q=d[b].q||[]).push(arguments)};e=a.createElement(l);
e.async=1;e.charset='utf-8';e.src='//static.dable.io/dist/plugin.min.js';
_=a.getElementsByTagName(l)[0];_.parentNode.insertBefore(e,_);
})(window,document,'dable','script');
</script>
<link rel="stylesheet" href="/plugins/bootstrap-5.3.3/css/bootstrap.css">
<link rel="stylesheet" href="/openads3/css/fonts/Pretendard/fonts.css">
<link rel="stylesheet" href="/openads3/css/plugin.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/sun-typeface/SUIT@2/fonts/variable/woff2/SUIT-Variable.css">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/openads3/css/root.css">
<link rel="stylesheet" href="/openads3/css/common.css">
<link rel="stylesheet" href="/openads3/css/layout.css">
<!-- 오픈애즈 데이터 그리드 관련 설정 -->
<script>
// 1. 변수
var datagridMethods = $.fn.datagrid.methods;
var datagridDefaultView = $.fn.datagrid.defaults.view;
var treegridDefaultView = $.fn.treegrid.defaults.view;
// 2. 플러그인
// 2-1. easyui
// 2-1-1. datagrid
$.fn.datagrid.defaults.lazy = false;
$.fn.datagrid.defaults.striped = true;
$.fn.datagrid.defaults.fitColumns = true;
$.fn.datagrid.defaults.fixedHeight = false;
$.fn.datagrid.defaults.loadMsg = null;
$.fn.datagrid.defaults.singleRow = false;
$.fn.datagrid.defaults.selectable = true;
$.fn.datagrid.defaults.data = [];
$.fn.datagrid.defaults.emptyMsg = '<div class="datagrid-empty" style="top: 34px;"><i class="ico ico-no-data"></i><p class="fz-18 fc-10">조회된 데이터가 없습니다.</p><p class="fz-13 fc-4">다시 검색하거나 다른 필터를 사용하세요.</p></div>';
$.fn.datagrid.defaults.emptyHeight = 400;
$.fn.datagrid.defaults.totalMsg = '합계';
$.fn.datagrid.defaults.propertyRows = 'content';
$.fn.datagrid.defaults.propertyTotal = 'total';
$.fn.datagrid.defaults.propertyFooter = 'footer';
$.fn.datagrid.defaults.scrollbarSize = 0;
$.fn.datagrid.defaults.rowHeight = 54;
$.fn.datagrid.defaults.rownumberWidth = 40;
$.fn.datagrid.defaults.view = $.extend({}, $.fn.datagrid.defaults.view, {
onBeforeRender: function(target, rows) {
var options = $(target).datagrid('options');
if(!options.fixedHeight) {
$(target).datagrid('resize', { height: rows.length === 0 ? options.emptyHeight : 'auto' });
}
},
renderRow: function(target, fields, frozen, rowIndex, rowData) {
fields.forEach(function(field) {
var columnOption = $(target).datagrid('getColumnOption', field);
if(columnOption.escape) {
rowData[field] = ut.escapeHtml(rowData[field]);
}
});
return datagridDefaultView.renderRow.call(this, target, fields, frozen, rowIndex, rowData);
},
onAfterRender: function(target) {
datagridDefaultView.onAfterRender.call(this, target);
$(window).resize();
}
});
$.fn.datagrid.defaults.onBeforeLoad = function(param) {
var options = $(this).datagrid('options');
if(options.lazy) {
options['lazy'] = false;
return false;
}
if(param.sort) {
var field = options.columns[0].findByKey('field', param.sort);
if(ut.isEmpty(field)){
if(options.columns[1])
field = options.columns[1].findByKey('field', param.sort);
}
if(field) {
if(field.sort) {
param['sort'] = field.sort + ',' + param.order;
} else {
param['sort'] = (field.sortConvert == false ? param.sort : param.sort.camel2under()) + ',' + param.order;
}
} else {
param['sort'] = param.sort.camel2under() + ',' + param.order;
}
delete param.order;
}
if(options.paginationId) {
var pagerData = $('#' + options.paginationId).epePagination('getPagerData');
$.extend(param, pagerData);
}
}
$.fn.datagrid.defaults.loader = function(param, success, error) {
var options = $(this).datagrid('options');
if(!options.url) return false;
$.send(options.url, param, function(json) {
if(options.singleRow) json = (json == null) ? [] : [json];
success(json);
});
}
$.fn.datagrid.defaults.onBeforeProcessRows = function(rows) {
}
$.fn.datagrid.defaults.onBeforeSortColumn = function(sort, order) {
var options = $(this).datagrid('options');
if(options.paginationId) {
$('#' + options.paginationId).epePagination('initPagerData');
}
}
$.fn.datagrid.defaults.processRows = function(rows) {
var options = $(this).datagrid('options');
//yhlee custom
var state = $.data(this, 'datagrid');
var opts = state.options;
var $checkBox = state.dc.view2.find('.datagrid-header-check input[type=checkBox]');
if($checkBox.length !=0 && !ut.isEmpty(opts.headerCheckBoxId)){
$checkBox.attr('id', opts.headerCheckBoxId);
}
options.onBeforeProcessRows.call(this, rows);
if(rows == null || (rows.length == 0 && options.dialogId) ) {
setTimeout(function(){
$('#' + options.dialogId).dialog('center').dialog('resize');
}, 0);
}
return rows;
}
$.fn.datagrid.defaults.onBeforeProcessFooter = function(footer) {
}
$.fn.datagrid.defaults.processFooter = function(footer) {
var options = $(this).datagrid('options');
var first = options.columns[0][0];
options.onBeforeProcessFooter.call(this, footer);
if(footer instanceof Array) {
footer.forEach(function(item) {
item['isFooter'] = true;
item[first.field] = options.totalMsg;
});
return footer;
} else {
footer['isFooter'] = true;
footer[first.field] = options.totalMsg;
return [footer];
}
}
$.fn.datagrid.defaults.loadFilter = function(data) {
var options = $(this).datagrid('options');
if(data instanceof Array) return options.processRows.call(this, data);
var rows = options.processRows.call(this, data.hasOwnProperty('rows') ? data.rows : data[options.propertyRows]);
if(rows.length == 0) return { rows: [], total: 0, footer: [] };
var result = { rows: rows };
if(data.hasOwnProperty('total') || data.hasOwnProperty(options.propertyTotal)) {
result['total'] = data.hasOwnProperty('total') ? data.total : data[options.propertyTotal];
}
if(data.hasOwnProperty('footer') || data.hasOwnProperty(options.propertyFooter)) {
result['footer'] = options.processFooter.call(this, data.hasOwnProperty('footer') ? data.footer : data[options.propertyFooter]);
}
return result;
}
$.fn.datagrid.defaults.onLoadSuccess = function(data) {
var options = $(this).datagrid('options');
if(options.paginationId) {
$('#' + options.paginationId).epePagination('refresh', data.total);
}
if(options.totalId) {
var text = data.total;
$('#' + options.totalId).text(ut.format(text));
}
data.rows.forEach(function(row) {
if(!ut.isEmpty(row.tooltip_list)){
row.tooltip_list.forEach(function(item, index){
item.id.toDom().tooltip({
content: item.content
});
})
}
});
options.onAfterLoadSuccess.call(this, data);
/**
* 기본적으로 autoMaxHeight는 버퍼뷰랑 같이 쓰는 로직
* 편의를 위해 로컬 데이터 로드시에도 사용 가능한 로직을 추가한다. autoMaxHeightLocal
*/
var $this = $(this);
var state = $.data(this, 'datagrid');
var dc = state.dc;
setTimeout(function(){
if(options.autoMaxHeightLocal){
var pageRow = Boolean(options.pageRow) ? Number(options.pageRow) : 10;
if(data.rows.length >= pageRow){
var maxHeightValue = 0;
dc.view2.children().each(function(){
if($(this)[0].className == 'datagrid-header'){
$(this).find('tr').each(function(){
maxHeightValue += Number($(this).height());
});
}
if($(this)[0].className == 'datagrid-body'){
var rowCnt = 0;
$(this).find('tr').each(function(){
if(++rowCnt > pageRow) return;
maxHeightValue += Number($(this).height());
});
}
if($(this)[0].className == 'datagrid-footer'){
$(this).find('tr').each(function(){
maxHeightValue += Number($(this).height());
});
}
});
$this.datagrid('resize', { maxHeight: ++maxHeightValue });
}else{
$this.datagrid('resize', { maxHeight: null });
}
}
if(options.dialogId) {
$('#' + options.dialogId).dialog('center').dialog('resize');
}
}, 0);
}
$.fn.datagrid.defaults.onAfterLoadSuccess = function(data) {
}
$.fn.datagrid.defaults.onBeforeFetch = function() {
}
$.fn.datagrid.defaults.onFetch = function() {
}
$.fn.datagrid.defaults.onBeforeAppendRow = function() {
}
$.fn.datagrid.defaults.onAfterAppendRow = function() {
}
$.fn.datagrid.defaults.onBeforeDeleteRow = function() {
}
$.fn.datagrid.defaults.onAfterDeleteRow = function() {
}
$.fn.datagrid.defaults.onBeforeSelect = function() {
if(!$(this).datagrid('options').selectable){
return false;
}
}
$.fn.datagrid.methods = $.extend({}, $.fn.datagrid.methods, {
load: function(jq, param) {
var options = jq.datagrid('options');
if(options.paginationId) {
$('#' + options.paginationId).epePagination('initPagerData');
}
if(options.autoMaxHeight){
options.maxHeight = null;
}
$(jq).datagrid('reload', param);
},
reloadData: function(jq) {
var rows = jq.datagrid('getRows');
$(jq).datagrid('loadData', rows);
},
getSortData: function(jq) {
var options = jq.datagrid('options');
return { sort: options.sortName.camel2under() + ',' + options.sortOrder };
},
appendRow: function(jq, row) {
var options = jq.datagrid('options');
options.onBeforeAppendRow.call(jq, row);
datagridMethods.appendRow(jq, row);
options.onAfterAppendRow.call(jq, row);
return jq;
},
deleteRow: function(jq, row, index) {
var options = jq.datagrid('options');
options.onBeforeDeleteRow.call(jq, row, index);
datagridMethods.deleteRow(jq, row, index);
options.onAfterDeleteRow.call(jq, row, index);
return jq;
}
});
// 2-1-2. treegrid
$.fn.treegrid.defaults.striped = true;
$.fn.treegrid.defaults.fitColumns = true;
$.fn.treegrid.defaults.fixedHeight = false;
$.fn.treegrid.defaults.loadMsg = null;
$.fn.treegrid.defaults.singleRow = false;
$.fn.treegrid.defaults.emptyMsg = '검색 결과가 없습니다.';
$.fn.treegrid.defaults.emptyHeight = 300;
$.fn.treegrid.defaults.propertyRows = 'content';
$.fn.treegrid.defaults.propertyTotal = 'total';
$.fn.treegrid.defaults.propertyFooter = 'footer';
$.fn.treegrid.defaults.scrollbarSize = 0;
$.fn.treegrid.defaults.rowHeight = 54;
$.fn.treegrid.defaults.rownumberWidth = 40;
$.fn.treegrid.defaults.view = $.extend({}, $.fn.treegrid.defaults.view, {
onBeforeRender: function(target, row, data) {
var options = $(target).treegrid('options');
if(!options.fixedHeight) {
$(target).treegrid('resize', { height: data.length == 0 ? options.emptyHeight : 'auto' });
}
treegridDefaultView.onBeforeRender.call(this, target, row, data);
},
onAfterRender: function(target) {
treegridDefaultView.onAfterRender.call(this, target);
$(window).resize();
}
});
$.fn.treegrid.defaults.onBeforeLoad = function(row, param) {
var options = $(this).treegrid('options');
param[options.idField] = param.id;
delete param.id;
}
$.fn.treegrid.defaults.loader = function(param, success, error) {
var options = $(this).treegrid('options');
if(!options.url) return false;
$.send(options.url, param, function(json) {
success(json);
});
}
$.fn.treegrid.defaults.onBeforeProcessRows = function(data, parentId) {
}
$.fn.treegrid.defaults.processRows = function(data, parentId) {
var options = $(this).treegrid('options');
options.onBeforeProcessRows.call(this, data, parentId);
data.forEach(function(item) {
item['state'] = options.state(item) ? 'open' : 'closed';
});
return data;
}
$.fn.treegrid.defaults.onBeforeProcessFooter = function(footer) {
}
$.fn.treegrid.defaults.processFooter = function(footer) {
var options = $(this).treegrid('options');
options.onBeforeProcessFooter.call(this, footer);
if(footer instanceof Array) {
footer.forEach(function(item) {
item['isFooter'] = true;
});
return footer;
} else {
footer['isFooter'] = true;
return [footer];
}
}
$.fn.treegrid.defaults.loadFilter = function(data, parentId) {
var options = $(this).treegrid('options');
if(data instanceof Array) return options.processRows.call(this, data, parentId);
var rows = options.processRows.call(this, data.hasOwnProperty('rows') ? data.rows : data[options.propertyRows]);
if(rows.length == 0) return { rows: [], footer: [] };
var result = { rows: rows };
if(data.hasOwnProperty('footer') || data.hasOwnProperty(options.propertyFooter)) {
result['footer'] = options.processFooter.call(this, data.hasOwnProperty('footer') ? data.footer : data[options.propertyFooter]);
}
return result;
}
$.fn.treegrid.defaults.onLoadSuccess = function(row, data) {
var options = $(this).treegrid('options');
if(options.totalId && row == null) {
var text = data instanceof Array ? data.length : data.rows.length;
$('#' + options.totalId).html(ut.format(text));
}
options.onAfterLoadSuccess.call(this, row, data);
}
$.fn.treegrid.defaults.state = function(item) {
return false;
}
$.fn.treegrid.defaults.onAfterLoadSuccess = function(row, data) {
}
// 2-1-3. tree
/* $.fn.tree.defaults.loader = function(param, success, error) {
var options = $(this).tree('options');
if(!options.url) return false;
$.send(options.url, param, success);
}
$.fn.treegrid.defaults.onBeforeProcessRows = function(data, parent) {
}
$.fn.tree.defaults.processRows = function(data, parent) {
var options = $(this).tree('options');
options.onBeforeProcessRows.call(this, data, parent);
data.forEach(function(item) {
item['state'] = options.state(item) ? 'open' : 'closed';
});
return data;
}
$.fn.tree.defaults.loadFilter = function(data, parent) {
var options = $(this).tree('options');
return options.processRows.call(this, data, parent);
} */
Handlebars.registerHelper('eq', function(arg1, arg2) {
return (arg1 == arg2);
});
</script>
<!-- Facebook Pixel Code -->
<script>
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '1278682068838779');
fbq('track', 'PageView');
</script>
<noscript>
<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1278682068838779&ev=PageView&noscript=1" />
</noscript>
<!-- End Facebook Pixel Code -->
<!-- 애드센스 -->
<script data-ad-client="ca-pub-2903995566328650" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- 애드센스 끝 -->
<!-- 구글 애널리틱스 -->
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-SQ2XP2KTF0"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-SQ2XP2KTF0');
</script>
<!-- 구글 애널리틱스 끝 -->
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-WMG36JXS');
</script>
<!-- End Google Tag Manager -->
<!-- more 스크립트 -->
<script>
var _croID = '5dafdc01e4b0d10c3f3b932e';
function getMoreRecommend(e,n){"undefined"!=typeof globalCRO?new globalCRO.MoreRecommendData(e,n):
("undefined"==typeof gCroRCData&&(window.gCroRCData=new Array),gCroRCData.push({config:e,callback:n}))}
function createMoreRCView(e){"undefined"!=typeof globalCRO?new globalCRO.MoreRecommendView(e):
("undefined"==typeof gCroRCV&&(window.gCroRCV=new Array),gCroRCV.push({config:e}))}
function mcroPushEvent(n){"undefined"!=typeof globalCRO?globalCRO.sendEvent(n):
("undefined"==typeof gCro&&(window.gCro=new Array),gCro.push(n))}
function _cro_initialize(){window.globalCRO=new MCro,globalCRO.jsInit(_croID)}
</script>
<script async type="text/javascript" src="//cro.myshp.us/resources/common/js/more-common.js"></script>
<!-- more 스크립트 끝 -->
</head>
<body>
<!-- more 티커 스크립트 -->
<!-- more 티커 스크립트 끝 -->
<div class="wrap">
<header class="header">
<!-- JSTL -->
<!-- SPRING -->
<div class="inner">
<a class="navbar-brand" href="/">Open Ads</a>
<!-- Container : Start -->
<div class="container-xxl">
<!-- GNB : Start -->
<!-- GNB 개편(2026-07): 탐색·지금,마케팅·실행도구·흐름과 판단·인사이터·O'story / 인기·AI툴추천·프롬프트 미노출(데이터 보존) -->
<ul class="navbar-nav gnb">
<li class="nav-item">
<!-- DropDown (Category=탐색) : Start -->
<div class="dropdown category">
<a class="nav-link dropdown-toggle" id="category" role="button" data-bs-toggle="dropdown"
aria-expanded="false">
<i class="ico ico-cube"></i>탐색
</a>
<ul class="dropdown-menu" id="categoryDropDownUl"></ul>
</div>
<!-- DropDown (Category=탐색) : End -->
</li>
<li class="nav-item">
<!-- DropDown (Depth-Menu) : Start -->
<div class="dropdown depth-menu">
<a class="nav-link dropdown-toggle active" role="button" data-bs-toggle="dropdown" aria-expanded="false">
지금, 마케팅
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item active" href="/latest" id="latest"><span class="text-dot">방금 올라온</span></a></li>
<li><a class="dropdown-item" href="/popular" id="popular"><span class="text-dot">요즘 뜨는</span></a></li>
<li><a class="dropdown-item" href="https://markethinking.stibee.com/" target="_blank" rel="noopener">마케띵킹👀</a></li>
</ul>
</div>
<!-- DropDown (Depth-Menu) : End -->
</li>
<li class="nav-item">
<!-- 흐름과 판단 (구 카테고리 '행사' 승격, 드롭다운 없이 바로 진입) -->
<a class="nav-link" href="/festival/festivalList" id="festivalList">흐름과 판단</a>
</li>
<li class="nav-item">
<a class="nav-link" id="insighterList" onclick="location.href='/insighterList'">인사이터</a>
</li>
<li class="nav-item">
<!-- DropDown (실행도구 = 구 광고상품정보 + AI툴 병합) : Start -->
<div class="dropdown ad-prd-info">
<a class="nav-link dropdown-toggle active" role="button" data-bs-toggle="dropdown"
aria-expanded="false">
실행도구
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" id="item" href="/item">광고상품/솔루션</a></li>
<li><a class="dropdown-item" id="benefit" href="/benefit">할인혜택</a></li>
<li><a class="dropdown-item" id="aiToolSearch" href="/aiDirectory/aiToolSearch">AI툴</a></li>
</ul>
</div>
<!-- DropDown (실행도구) : End -->
</li>
<li class="nav-item">
<!-- DropDown (O-Story, 맨 마지막) : Start -->
<div class="dropdown o-story">
<a class="nav-link dropdown-toggle active" role="button" data-bs-toggle="dropdown" aria-expanded="false">
O'story
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item gnb-track" data-menu-key="ostory" href="/ostory">O'story 소개</a></li>
<li><a class="dropdown-item gnb-track" data-menu-key="brand_archive" href="https://openads.co.kr/insighter/insighterDetail?authorId=701">브랜드 아카이브</a></li>
</ul>
</div>
<!-- DropDown (O-Story) : End -->
</li>
</ul>
<!-- GNB : End -->
<!-- Util : Start -->
<ul class="navbar-nav util">
<li class="nav-item">
<button type="button" class="nav-link" data-bs-toggle="offcanvas" data-bs-target="#offcanvasTop"
aria-controls="offcanvasTop">
<i class="ico ico-search"></i>
</button>
</li>
<li class="nav-item">
<a class="nav-link" href="/join/login">로그인</a>
</li>
<li class="nav-item d-none" id="headerNewsLetterSubscribeLi">
<div class="btn-group" role="group">
<button type="button" class="btn btn-primary" id="headerNewsLetterSubscribeBtn">뉴스레터 구독
</button>
</div>
</li>
<li class="nav-item d-none d-xl-block">
<div class="btn-group" role="group">
<button type="button" class="btn btn-outline-secondary"
onclick="location.href='/insighter/insighterCMS/insighterApply'">인사이터 신청
</button>
</div>
</li>
</ul>
<!-- Util : End -->
<!-- Global-Search : Start -->
<div class="offcanvas offcanvas-top global-search" tabindex="-1" id="offcanvasTop"
aria-labelledby="global-search">
<div class="offcanvas-body" id="global-search">
<div class="inner">
<div class="container-xxl">
<div class="row">
<div class="col-12">
<!-- Input-Group : Start -->
<div class="input-group">
<input id="keywordText" type="text" class="form-control" placeholder="검색어를 입력하세요."
aria-label="Search">
<div class="box-right">
<button class="btn btn-link px-0" type="button" id="searchButton">
<i class="ico ico-search sz-32 sz-xl-40"></i>
</button>
</div>
</div>
<!-- Input-Group : End -->
<div class="group flex-xl-row">
<div class="box">
<span class="fs-12 fs-xl-13 fw-bold fc-gray-100">인기 검색어</span>
</div>
<div class="box flex-wrap">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Global-Search : End -->
</div>
<!-- Container : End -->
</div>
<!-- ################# 템플릿 모음 ################# -->
<script type="text/x-handlebars-template" id="newPostInsighterRowTemplate">
<a href="/insighter/insighterDetail?authorId={{authorId}}">
<img src="{{imgFilePath}}{{imgFileName}}" class="img-thumbnail object-fit-cover rounded-circle sz-36"
alt="인사이터 프로필">
</a>
</script>
<script>
$(function () {
const headerInit = () => {
//카테고리 로드
loadCategories();
//알람 세션 리프래시
alartSessionRefresh();
// 알람(종)아이콘 클릭 > 새 글을 발행한 인사이터 데이터 로드
loadNewPubContentInsighters();
// 알림 여부
loadSetting();
initGnb();
}
/* 카테고리 드롭다운 옵션 load */
const loadCategories = () => {
// categoryCode별 커스텀 클래스 매핑
const CATEGORY_CLASS_MAP = {
'CC97': 'fc-orange-110',
// 'IST01': 'fc-blue-80'
};
// categoryType별 URL 생성
const getCategoryUrl = (category) => {
const {categoryType, categoryCode} = category;
if (categoryType === "FESTIVAL") {
return "/festival/festivalList";
} else if (categoryType === "INSIGHTER") {
return "/insighterList";
} else {
return "/content?category=" + categoryCode;
}
};
// 카테고리 클래스 생성
const getCategoryClasses = (categoryCode) => {
const classes = [];
if (categoryCode === "CC49") {
classes.push("active");
}
if (CATEGORY_CLASS_MAP[categoryCode]) {
classes.push(CATEGORY_CLASS_MAP[categoryCode]);
}
return classes.join(' ');
};
// 카테고리 HTML 생성
const createCategoryRow = (category, categoryCode, classes) => {
const rowTemplate = '<li><a class="dropdown-item {4} category-menu" href="{1}" id="{3}">{2}</a></li>';
const href = getCategoryUrl(category);
return ou.formatString(rowTemplate, href, category.categoryName, categoryCode, classes);
};
$.send('/gnb/header/findCategories', {}, function (json) {
let categoryRows = '';
let hasDividerInserted = false;
json.forEach(category => {
const {categoryType, categoryCode} = category;
// INSIGHTER 타입 전에 divider 추가
/*
if (categoryType === "INSIGHTER" && !hasDividerInserted) {
categoryRows += '<li class="divider"></li>';
hasDividerInserted = true;
}
*/
// INSIGHTER · FESTIVAL('흐름과 판단'으로 GNB 승격) 카테고리는 탐색 목록에서 제외
if (categoryType === "INSIGHTER" || categoryType === "FESTIVAL") return;
const classes = getCategoryClasses(categoryCode);
categoryRows += createCategoryRow(category, categoryCode, classes);
});
$('#categoryDropDownUl').append(categoryRows);
activeClassByUrl();
});
}
/* v3 경로에 따라 active 클래스 추가 이벤트 */
const activeClassByUrl = () => {
const currentUrl = new URL(window.location.href);
const activeCategory = currentUrl.searchParams.get('category');
const pathSegments = currentUrl.pathname.split('/').filter(Boolean); // ['', 'aiDirectory', 'aiToolSearch'] → ['aiDirectory', 'aiToolSearch']
const subPath = pathSegments[1] || pathSegments[0] || ''; // 두 번째 path가 있으면 사용, 없으면 첫 번째 path 사용
// 카테고리 페이지 클래스 바인딩
if (activeCategory) {
$('.category-menu').removeClass('active');
$('#' + activeCategory).addClass('active');
return;
}
// 전체 메뉴에서 active 제거
$('#latest, #item, #benefit, #aiToolSearch, #festivalList').removeClass('active');
// ✅ 하위 메뉴만 active 처리
if (subPath) {
$('#' + subPath).addClass('active');
}
}
/* 알람 세션 리프레시 */
const alartSessionRefresh = () => {
$.send2('/member/getAlarmSession', {}, function () {
});
}
/* 알림설정 */
const loadSetting = () => {
$.send2("/member/mypage-setting-getSetting", {}, function (json) {
if (!ut.isEmpty(json)) {
const settingsList = json.settingList;
const setting1 = settingsList.find(s => s.settingId === 'setting-1');
const setting1AlertYn = setting1.ALERT_YN === 'Y';
const exceptionIds = ['setting-8', 'setting-12', 'setting-27'];
// 각 설정 항목에 대한 반복문
settingsList
.filter(setting => exceptionIds.includes(setting.settingId))
.forEach(setting => {
const isChecked = setting.ALERT_YN === 'Y' || setting1AlertYn;
$("#setting-ALL").prop("checked", isChecked);
});
}
});
}
/* 알림 업데이트 */
const updateSetting = (target) => {
const memberAlertTypeId = target.data('setting-value');
const targetVal = target.prop("checked");
$.send2("/member/mypage-setting-updateSetting", {
memberAlertTypeId: memberAlertTypeId,
targetVal: targetVal,
memberId: 0
}, function (message) {
}, function (_) {
om.msg('다시 시도해주세요.')
});
}
/* 새 글을 발행한 인사이터 목록 로드 */
const loadNewPubContentInsighters = () => {
$.send("/member/findListNewPubContentInsighters", {"maxInsighterCnt": 5}, function (json) {
if (ut.isEmpty(json)) {
$("#newPubInsighterDiv").addClass('d-none');
return;
}
$.each(json, function (index, insighter) {
$("#newPubInsighterRow").append(ou.handlebars(insighter, "newPostInsighterRowTemplate"));
})
})
}
/** 카테고리 클릭 시 수치 증가 */
const addCategoryClickCntEvent = (categoryCode) => {
if (!ut.isEmpty(categoryCode)) {
$.send2("/content/addCategoryClickCnt", {categoryId: categoryCode}, function (json) {
});
}
}
/* v3 알림 읽음 처리 이벤트 */
const alertClickEvent = (target) => {
const memberAlertId = target.data('member-alert-id');
const moveYn = target.data('move-yn');
const moveUrl = target.data('move-url');
$.send2('/member/readAlarm', {memberAlertId: memberAlertId}, function (json) {
if (moveYn) location.href = moveUrl;
});
}
/* v3 검색 이벤트 */
const searchEvent = () => {
const keywordText = $('#keywordText').val();
if (ut.isEmpty(keywordText)) {
om.msg('검색어를 입력하세요.');
return false;
}
location.href = "/search/result?type=contents&keyword=" + encodeURIComponent(keywordText);
}
/* v3 인기 검색어 클릭 이벤트 */
const keywordClickEvent = (target) => {
const keywordText = target.data('keyword')
location.href = "/search/result?type=contents&keyword=" + encodeURIComponent(keywordText.trim());
}
// 4. 이벤트
/* 카테고리 클릭 */
$(document).on('click', '.category-menu', function () {
const categoryCode = $(this).attr('id');
addCategoryClickCntEvent(categoryCode)
});
/* 업데이트 */
$('#setting-ALL').change(function () {
updateSetting($(this))
});
/* 알람 리스트 클릭 시 이벤트 */
$('.memberAlertRow').click(function () {
alertClickEvent($(this))
})
/* 검색어 검색 엔터 */
$('#keywordText').enter(searchEvent);
/* 검색 버튼(아이콘) 클릭*/
$('#searchButton').click(searchEvent);
/* 검색어 클릭 이벤트 */
$('.keyword').click(function () {
keywordClickEvent($(this))
})
// 초기화
headerInit()
// 모바일/태블릿 기기일 경우에만 뉴스레터 구독 버튼 노출
if (ou.isMobileDevice()) {
$('#headerNewsLetterSubscribeLi').removeClass('d-none');
}
// 헤더 뉴스레터 구독하기 클릭 시 이벤트
$('#headerNewsLetterSubscribeBtn').click(function () {
$.send2('/home/clickNewsLetterBanner', {bannerClickDiv: "MO_HEADER_BTN"}, function () {
const url = 'https://page.stibee.com/subscriptions/51974?groupIds=482104';
if (ou.isMobileDevice()) location.href = url;
else window.open(url);
})
})
})
</script>
</header>
<div class="content">
<!-- Cont-Detail : Start -->
<div class="cont-detail">
<!-- Section-01 : Start -->
<div class="section section-01">
<div class="inner">
<div class="container-xxl px-xxl-0">
<!-- 띠 베너 : Start -->
<div class="row">
<div class="col-12">
<!-- Img-Banner : Start -->
<div class="img-banner adBanner" data-bad-id="269" data-url="https://www.openads.co.kr/festival/festivalDetail?festivalId=14520128" style="cursor: pointer">
<img src="https://openads-real.s3.amazonaws.com/openadsAdmin/images/ad/제목을 입력하세요 (2)_20260803100534200.png" class="img-fluid object-fit-cover d-none d-xl-block" alt="광고">
</div>
<!-- Img-Banner : End -->
</div>
</div>
<!-- 띠 베너 : End -->
<!-- Box-Header : Start -->
<div class="row box-header">
<div class="col d-flex flex-column gap-24 gap-xl-32">
<div class="group flex-column gap-8 gap-xl-16">
<span class="fs-12 fs-xl-16 fc-gray-100">쇼핑몰 창업/운영</span>
<h2 class="fs-24 fs-xl-40 fw-bold fc-gray-100">자체 브랜드 제품개발, 실무에서 바로 쓰는 5단계 프로세스 정리</h2>
</div>
<div class="group flex-column flex-xl-row justify-content-xl-between gap-8">
<div class="box align-items-center gap-8 gap-xl-12">
<div class="d-flex align-items-center gap-6 gap-xl-8">
<a href="/insighter/insighterDetail?authorId=705">
<img src="https://openads-real.s3.amazonaws.com/openadsAdmin/images/authorImg/Frame 1.png" class="img-thumbnail object-fit-cover rounded-circle sz-24 sz-xl-32" alt="프로필">
</a>
<a href="/insighter/insighterDetail?authorId=705">
<span class="fs-13 fs-xl-16 fc-gray-100">원포인트(롸잇)</span>
</a>
</div>
<span class="fs-11 fs-xl-12 fc-gray-70">
2026.04.02 11:28
</span>
</div>
<div class="box gap-8 gap-xl-12">
<div class="d-flex align-items-center gap-4">
<i class="ico ico-view sz-24"></i>
<span class="fs-12 fc-gray-100">852</span>
</div>
<div class="d-flex align-items-center gap-4">
<button type="button" class="btn btn-ico "
id="contentLikeIcon">
<i class="ico ico-favorite sz-24"></i>
</button>
<span class="fs-12 fc-gray-100" id="contentLikeCntText">1</span>
</div>
<div class="d-flex align-items-center gap-4">
<a id="contentShareIcon"><i class="ico ico-share sz-24"></i></a>
<span class="fs-12 fc-gray-100" id="shareCntText">1</span>
</div>
<div class="d-flex align-items-center gap-4">
<a id="cmtIcon">
<i class="ico ico-comment sz-24"></i>
</a>
<span class="fs-12 fc-gray-100" id="topCmtCntText"></span>
</div>
<div class="d-flex align-items-center">
<button type="button" class="btn btn-ico content-bookmark " data-content-id="19105">
<i class="ico ico-bookmark sz-24"></i>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Box-Header : End -->
<!-- Box-Body : Start -->
<div class="row box-body">
<div class="col-xl-9 d-flex flex-column gap-40 gap-xl-64">
<div class="group">
<!-- 핵심요약 List-Group : Start -->
<ul class="list-group list-group-dot bg-gray-5 w-100">
<li class="list-group-item tit mb-0 border-bottom-0">
<span class="fs-16 fs-xl-20 fw-bold fc-gray-100">한눈에 보는 핵심요약</span>
</li>
<li class="fs-14 fs-xl-16 fc-gray-100" style="white-space: pre-line;">
제품개발 아이디어 검증부터 제품기획, 시제품제작, 양산, 출시까지 5단계 프로세스를 정리했습니다. 초기 브랜드가 자주 하는 실수와 현실적인 타임라인까지 확인해 보세요.
</li>
</ul>
<!-- 핵심요약 List-Group : End -->
</div>
<div class="group">
<pre-line>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">자기 브랜드 상품을 직접 만들어 파는 셀러가 빠르게 늘고 있어요. 외주 제작 인프라가 잘 갖춰져 있어서 아이디어만 있으면 누구든 도전할 수 있는 환경이 된 거죠.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">그런데 실무로 들어가면 이야기가 달라져요. 어디서부터 손을 대야 할지 감이 안 잡히는 경우가 대부분이에요. 제품개발에는 일정한 단계가 있고, 그 흐름을 먼저 파악해두면 불필요한 시행착오를 줄일 수 있어요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">아이디어 구상부터 실제 판매 개시까지, 초기 브랜드가 알아두면 좋을 제품개발프로세스를 다섯 단계로 정리해봤어요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<hr class="border-border-200 border-t-0.5 my-3 mx-1.5">
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"><strong><br></strong></p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"><strong><span style="font-family:Pretendard;font-size: 24pt;">검증 없는 아이디어는 재고로 돌아온다</span></strong></p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">신제품개발의 시작은 기획이 아니라 검증이에요. 시장에 수요가 있는지를 데이터로 먼저 확인하지 않으면, 결국 팔리지 않는 물건만 창고에 쌓이게 돼요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">검증 방법 자체는 복잡하지 않아요. 네이버 데이터랩이나 구글 트렌드에서 관련 키워드 검색량 흐름을 보고, 쿠팡이나 스마트스토어에서 유사 상품의 리뷰 수와 판매 추이를 살펴보면 시장 규모와 경쟁 강도를 가늠할 수 있어요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">여기서 놓치기 쉬운 포인트가 하나 있어요. 경쟁 제품의 별점 1점 리뷰를 분석하는 거예요. 소비자가 느끼는 불편, 기존 제품이 해결하지 못한 부분이 거기에 그대로 나와 있거든요. 이걸 파고들면 신제품의 차별화 방향이 잡혀요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<hr class="border-border-200 border-t-0.5 my-3 mx-1.5">
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"><strong><br></strong></p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"><strong><span style="font-family:Pretendard;font-size: 18pt;">제품기획 단계에서 차별점을 먼저 설계해야 하는 이유</span></strong></p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">시장 확인이 끝나면 상품개발 기획으로 넘어가요. 이 단계에서 빠지기 쉬운 함정이 있는데, 어떤 물건을 만들지에만 집중하는 거예요. 중요한 건 경쟁 제품 대비 어떤 차이를 줄 수 있느냐예요. 차별점이 없는 제품은 결국 가격으로만 경쟁하게 되고, 마진이 빠르게 무너져요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">초기 브랜드가 실무에서 자주 활용하는 차별화 방향은 네 가지 정도로 나뉘어요. 디자인에 브랜드 감성을 입히는 방식, 리뷰 불만에서 나온 문제를 스펙으로 해결하는 기능 개선, 원료 산지나 개발 철학을 내세우는 스토리텔링, 세트 구성으로 체감 가치를 높이는 번들링이에요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">이 시점에 간단한 콘셉트 문서를 만들어두면 이후 공장 소통에서 커뮤니케이션 비용이 확 줄어요. 타깃 고객, 핵심 차별점, 희망 판매가, 원가 범위 정도만 한 장으로 정리하면 충분해요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<hr class="border-border-200 border-t-0.5 my-3 mx-1.5">
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"><strong><br></strong></p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"><strong><span style="font-family:Pretendard;font-size: 18pt;">시</span></strong><strong style="font-family:Pretendard;font-size: 12pt;"><span style="font-family:Pretendard;font-size: 18pt;">제품 제작, 샘플 비교 없이 양산으로 넘어가면 안 되는 이유</span></strong></p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">기획 정리가 끝나면 실물 제작에 들어가요. 일반적인 흐름은 레퍼런스 자료를 공장에 전달하고, 1차 샘플을 받아 스펙과 마감을 확인한 뒤, 피드백을 반영해 2~3차 수정을 거쳐 최종 골드 샘플을 확정하는 순서예요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">실무에서 자주 보이는 실수가 있어요. 첫 샘플이 만족스럽다고 곧바로 양산을 진행하는 건데, 반드시 2~3곳 이상 공장에서 샘플을 받아 비교해야 해요. 같은 조건을 전달해도 공장마다 결과물 품질이 상당히 달라요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">샘플을 검토할 때는 시안과 실물의 색상 오차, 소재의 촉감과 내구성, 포장 상태에서의 배송 파손 위험, 해당 카테고리 필수 인증 여부(KC인증, 식약처 등)를 빠짐없이 체크해야 해요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<hr class="border-border-200 border-t-0.5 my-3 mx-1.5">
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"><strong><br></strong></p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"><strong><span style="font-family:Pretendard;font-size: 18pt;">양산에서 초도 물량 조절이 중요한 진짜 이유</span></strong></p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">골드 샘플 확정 후 본격 제품양산에 들어가면, 가장 주의할 부분은 초도 수량이에요. 아직 시장 반응이 검증되지 않은 상태에서 대량 생산을 걸면 현금 흐름이 재고에 묶여요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">현실적인 양산 전략은 단계적으로 가는 거예요. 1차에서는 최소 MOQ로 시장 반응을 확인하고, 2차에서는 판매 데이터에 따라 수량을 조정해요. 안정적인 판매 흐름이 확인되면 그때 물량을 늘려서 단가 협상에 들어가는 게 안전해요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">양산 중에도 품질 관리는 멈추면 안 돼요. 생산 라인 초반 제품을 반드시 직접 확인하고, 불량률 허용 범위는 사전에 계약으로 잡아두는 게 리스크를 줄이는 방법이에요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<hr class="border-border-200 border-t-0.5 my-3 mx-1.5">
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"><strong><br></strong></p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"><strong><span style="font-family:Pretendard;font-size: 18pt;">출시 전 세팅을 빠뜨리면 제품이 좋아도 안 팔린다</span></strong></p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">제품이 완성된 것과 판매가 가능한 것은 다른 문제예요. 출시 전에 갖춰야 할 요소가 꽤 있어요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">상표권 출원은 제조에 들어가기 전에 먼저 진행하는 게 안전하고, 상세페이지 기획과 제작, 제품 촬영, 판매 채널 입점(스마트스토어, 쿠팡, 자사몰 등), 초기 마케팅 전략(SNS, 인플루언서, 체험단 등)까지 세팅이 끝나야 실제 판매를 시작할 수 있어요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">특히 상세페이지는 소비자가 제품을 처음 만나는 접점이라 기획에 충분한 시간을 들여야 하고요. 마케팅은 출시 당일이 아니라 2~4주 전부터 사전 노출을 쌓아가는 게 초기 트래픽 확보에 효과적이에요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<hr class="border-border-200 border-t-0.5 my-3 mx-1.5">
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"><strong><br></strong></p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"><strong><span style="font-family:Pretendard;font-size: 18pt;">전체 소요 기간</span></strong></p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">아이디어 검증에 1~2주, 제품기획에 1~2주, 시제품 제작에 4~8주, 양산에 2~6주, 출시 준비에 2~3주 정도가 걸려요. 카테고리와 제조 방식에 따라 편차는 있지만, 전체적으로 약 3~5개월이 일반적인 타임라인이에요.</p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<hr class="border-border-200 border-t-0.5 my-3 mx-1.5">
<p> </p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]"> </p>
<p class="font-claude-response-body break-words whitespace-normal leading-[1.7]">제품개발 전체 흐름을 잡아두면 각 단계에서 어디에 리소스를 집중해야 할지가 분명해져요. 그런데 이 프로세스를 실행에 옮기려면 한 가지 더 결정할 게 있어요. 제조 방식에 따라 비용 구조와 일정, 품질 컨트롤 범위가 완전히 달라지거든요. OEM과 ODM 방식의 차이와 초기 브랜드에 맞는 선택 기준은 다음 글에서 이어서 풀어볼게요.</p>
<p> </p>
</pre-line>
</div>
<div class="group flex-wrap gap-8">
<span class="tag fs-13 fs-xl-14 fc-gray-100">#제품개발</span>
<span class="tag fs-13 fs-xl-14 fc-gray-100">#제품기획</span>
<span class="tag fs-13 fs-xl-14 fc-gray-100">#상품개발</span>
<span class="tag fs-13 fs-xl-14 fc-gray-100">#제품양산</span>
<span class="tag fs-13 fs-xl-14 fc-gray-100">#제품개발프로세스</span>
</div>
<div class="group card-help flex-column gap-32 gap-xl-48">
<!-- Card : Start -->
<div class="card border d-flex flex-xl-row justify-content-between align-items-xl-center">
<div class="card-header d-flex align-items-center gap-12">
<div class="group">
<a class="card-img rounded-circle sz-64" href="/insighter/insighterDetail?authorId=705">
<img src="https://openads-real.s3.amazonaws.com/openadsAdmin/images/authorImg/Frame 1.png" class="img-thumbnail object-fit-cover" alt="프로필">
</a>
</div>
<div class="group flex-column gap-xl-4">
<a href="/insighter/insighterDetail?authorId=705">
<p class="fs-16 fs-xl-18 fw-bold fc-gray-100">원포인트(롸잇)</p>
<p class="fs-13 fc-gray-70 text-truncate">탑티어 마케터 파트타임 고용 서비스, 원포인트입니다. 원포인트에서 활동 중인 업계 10% 탑티어 마케터의 다양한 인사이트와 업무 효율화, 프리랜서 시장에 대한 여러 소식을 전합니다</p>
</a>
</div>
</div>
<div class="card-body">
<button type="button" class="btn btn btn-outline-secondary h-40 w-100 " id="subscribeAuthorButton">
알림받기
</button>
</div>
</div>
<!-- Card : End -->
<div class="box flex-column gap-16 gap-xl-24">
<span class="fs-16 fs-xl-20 fw-bold fc-gray-100 text-center">이 콘텐츠가 도움이 되셨나요?</span>
<div class="d-flex gap-8 m-auto">
<button type="button" class="btn btn-outline-secondary" id="helpfulButton" data-value="CONTENT_HELPFUL">도움돼요</button>
<button type="button" class="btn btn-outline-secondary" id="notGoodButton" data-value="CONTENT_NOT_GOOD">아쉬워요</button>
</div>
</div>
</div>
<!-- 댓글 Section : Start -->
<div class="group comment flex-column gap-16 gap-xl-24" id="commentSection">
<!-- 댓글 Header : Start -->
<div class="box flex-column">
<div class="d-flex">
<span class="fs-20 fs-xl-24 fw-bold fc-gray-100 position-relative">
이 글에 대한 의견을 남겨주세요!
<span class="badge-text round badge-lg bg-gray-100 fs-16 fw-bold fc-white" id="bottomCmtCntText"></span>
</span>
</div>
<span class="fs-13 fs-xl-14 fc-gray-70">서로의 생각을 공유할수록 인사이트가 커집니다.</span>
</div>
<!-- 댓글 Header : End -->
<!-- 댓글 입력 박스 : Start -->
<div class="box" id="contentCmtWriteDiv">
<div class="input-group">
<textarea class="form-control black" id="contentCmtTextArea" placeholder="댓글을 입력하세요." readonly></textarea>
<button type="button" class="btn btn-secondary h-100" id="cmtWriteButton">댓글남기기</button>
</div>
</div>
<!-- 댓글 입력 박스 : End -->
<!-- 내 댓글보기 스위치 영역 : Start-->
<div class="box my-comment">
<!-- Switch : Start -->
<div class="form-check form-switch black gap-8" id="myCommentSwitchDiv"></div>
<!-- Switch : End -->
</div>
<!-- 내 댓글보기 스위치 영역 : End-->
<!-- 댓글 영역 : Start-->
<div class="box comment-list flex-column" id="contentCmtListDiv"></div>
<!-- 댓글 영역 : End -->
</div>
<!-- 댓글 Section : End -->
</div>
<div class="col-xl-3">
<div class="group flex-column gap-xl-56">
<!-- 광고 Img-Banner : Start -->
<div class="img-banner adBanner" data-bad-id="258" data-url="https://www.openads.co.kr/cscenter/cscenter-service-intro" style="cursor: pointer">
<img src="https://openads-real.s3.amazonaws.com/openadsAdmin/images/ad/어바웃 오픈애즈_V4_20260724120150586.png" class="img-fluid object-fit-cover d-none d-xl-block" alt="광고">
</div>
<!-- 광고 Img-Banner : End -->
<!-- 함께 읽으면 좋은 인사이트 List-Group : Start -->
<ul class="list-group list-group-flush d-none d-xl-block" id="relatedContentDiv"></ul>
<!-- 함께 읽으면 좋은 인사이트 List-Group : End -->
<!-- Wrap-Adsense : Start -->
<div class="wrap-adsense ad-vertical">
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-2903995566328650"
crossorigin="anonymous"></script>
<!-- PC)콘텐츠상세_우측_수직형_900*1600 -->
<ins
class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-2903995566328650"
data-ad-slot="6057252161"
>
</ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
</div>
<!-- Wrap-Adsense : End -->
</div>
</div>
</div>
<!-- Box-Body : End -->
</div>
</div>
</div>
<!-- Section-01 : End -->
<!-- 주제/유형별 게시글 Section-02 : Start -->
<div class="section section-02 card-list" id="recommendContentSection">
<div class="inner">
<div class="container-xxl d-flex flex-column gap-32 gap-xl-40 px-xxl-0">
<div class="row box-header">
<div class="col d-flex justify-content-between">
<div class="group">
<h3 class="fs-20 fs-xl-28 fw-bold fc-gray-100">추천 콘텐츠</h3>
</div>
<div class="group">
<div class="swiper-button-next" id="recommendSwiperNext">
<a class="icon-link">
더보기<i class="ico ico-reloading"></i>
</a>
</div>
</div>
</div>
</div>
<div class="row box-body">
<div class="col" id="recommendListWrap">
<!-- Swiper : Start -->
<div class="swiper-container swiper-container-08">
<div class="swiper-wrapper" id="recommendSwiperWrapper"></div>
</div>
<!-- Swiper : End -->
</div>
<div class="col-12 d-none" id="recommendEmpty">
<p class="fs-16 fs-xl-24 fw-bold fc-gray-60 text-center empty">
관련 콘텐츠가 없습니다.
</p>
</div>
</div>
</div>
</div>
</div>
<!-- Section-02 : End -->
</div>
<!-- Cont-Detail : End -->
<!-- 공유하기 모달 -->
<div class="modal fade" id="shareModal" tabindex="-1" aria-labelledby="shareModalHeader" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="btn-close sz-24 sz-xl-32" data-bs-dismiss="modal" aria-label="Close"></button>
<h1 class="fs-20 fs-xl-24 fw-bold fc-orange-100" id="shareModalHeader">공유하기</h1>
</div>
<div class="modal-body">
<div class="group justify-content-center gap-16 gap-xl-24 py-2">
<a id="shareFb"><i class="ico logo-share-facebook sz-42 sz-xl-52"></i></a>
<a id="shareTw"><i class="ico logo-share-x sz-42 sz-xl-52"></i></a>
<a id="shareKa"><i class="ico logo-share-kakaotalk sz-42 sz-xl-52"></i></a>
<a id="shareNa"><i class="ico logo-share-naver sz-42 sz-xl-52"></i></a>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary h-40 h-xl-48" id="copyUrlButton">URL 복사</button>
</div>
</div>
</div>
</div>
<!-- ================== 템플릿 모음 ================== -->
<!-- 함께 읽으면 좋은 인사이트 템플릿 -->
<script type="text/x-handlebars-template" id="relatedContentRowTemplate">
<li class="list-group-item tit">
<span class="fs-16 fw-bold fc-gray-100">함께 읽으면 좋은 인사이트</span>
</li>
{{#each content}}
<li class="list-group-item">
<a href="/content/contentDetail?contsId={{contsId}}" class="d-flex flex-column gap-4">
<span class="fs-16 fw-medium fc-gray-100">{{title}}</span>
<span class="fs-12 fc-gray-100">{{authorName}}</span>
</a>
</li>
{{/each }}
</script>
<!-- 주제/유형별 추천 콘텐츠 슬라이드 템플릿 (카드 4개 = 1 슬라이드) -->
<script type="text/x-handlebars-template" id="recommendSlideTemplate">
<div class="swiper-slide">
<div class="row gap">
{{#each cards}}
<div class="col-6 col-xl-3">
<!-- Card : Start -->
<div class="card gap-16 gap-xl-24">
<div class="card-body gap-8 gap-xl-16">
<div class="group flex-column gap-4">
<p class="fs-11 fs-xl-14 fc-gray-100">{{subCategoryName}}</p>
<a class="stretched-link fs-14 fs-xl-24 fw-bold fc-gray-100 text-truncate" href="../content/contentDetail?contsId={{contsId}}">{{title}}</a>
</div>
<div class="group justify-content-between align-items-center">
<small class="fs-11 fs-xl-14 fc-gray-100 text-truncate">{{authorName}}</small>
<button type="button" class="btn btn-ico content-bookmark {{active}}" data-content-id="{{contsId}}">
<i class="ico ico-bookmark"></i>
</button>
</div>
</div>
<div class="card-img">
<img src="{{thumbFilePath}}{{thumbFileName}}" class="img-fluid object-fit-cover" alt="콘텐츠 썸네일">
</div>
</div>
<!-- Card : End -->
</div>
{{/each}}
</div>
</div>
</script>
<!-- 내 댓글 보기 스위치 템플릿-->
<script type="text/x-handlebars-template" id="myCommentSwitchTemplate">
<label class="form-check-label fs-14 fc-gray-100" for="myCommentSwitch">내 댓글 보기</label>
<input class="form-check-input" type="checkbox" role="switch" id="myCommentSwitch" {{#if disableYn}} disabled {{/if}}>
</script>
<!-- 댓글 / 대댓글 리스트 템플릿 -->
<script type="text/x-handlebars-template" id="commentTemplate">
{{#each cmtList}}
<div class="d-flex flex-column pe-xl-0">
<div class="d-flex flex-column pe-xl-0" id="cmtBox">
<div class="d-flex flex-column flex-xl-row flex-xl-wrap gap-12">
<div class="d-flex flex-xl-fill gap-12">
<img src="{{memberImgFilePath}}{{memberImgFileName}}"
class="img-thumbnail object-fit-cover rounded-circle sz-48" alt="프로필">
<div class="d-flex flex-column gap-2">
<div class="d-flex gap-4 align-items-center">
<span class="fs-14 fw-bold fc-gray-100">{{memberName}}</span>
{{#if myCommentYn}}
<span class="badge-text gray badge-sm fs-10 fw-medium fc-gray-100 bg-gray-10 myCommentTag d-none">내 댓글</span>
{{/if }}
</div>
<div class="d-flex gap-8">
<span class="fs-12 fs-xl-13 fc-gray-70">{{memberOcpnName}} {{career}}년차</span>
<span class="fs-12 fs-xl-13 fc-gray-70">{{convertRegDtime}}</span>
</div>
</div>
</div>
<div class="d-flex order-xl-2 w-xl-100">
<span class="fs-14 fc-gray-100 contentCmt" id="contentCmt-{{contsCmtId}}" style="white-space: pre-line;">{{content}}</span>
</div>
{{#if loginYn}}
<div class="d-flex justify-content-end gap-4">
<button type="button" class="btn btn-outline-secondary h-32 showReCommentBoxButton"
data-cmt-name="{{memberName}}"
data-conts-cmt-id="{{contsCmtId}}"
data-high-cmt-id="{{highContsCmtId}}">
댓글남기기
</button>
{{#if myCommentYn}}
{{> myComment}}
{{/if}}
</div>
{{/if }}
</div>
</div>
{{#if reCommentList}}
<!-- Reply : Start -->
<div class="d-flex flex-column reply">
<!-- 대댓글 작성 TEXT_AREA : Input-Group : Start -->
<div class="d-flex px-0" id="{{contsCmtId}}-contentReCmtWriteDiv"></div>
<!-- 대댓글 작성 TEXT_AREA : Input-Group : End -->
{{#each reCommentList}}
<!-- 대댓글 박스 : Input-Group : Start -->
<div class="d-flex flex-column pe-xl-0" id="recommentBox">
<div class="d-flex flex-column flex-xl-row flex-xl-wrap gap-12">
<div class="d-flex flex-xl-fill gap-12">
<img src="{{memberImgFilePath}}{{memberImgFileName}}"
class="img-thumbnail object-fit-cover rounded-circle sz-48" alt="프로필">
<div class="d-flex flex-column gap-2">
<div class="d-flex gap-4 align-items-center">
<span class="fs-14 fw-bold fc-gray-100">{{memberName}}</span>
{{#if myCommentYn}}
<span class="badge-text gray badge-sm fs-10 fw-medium fc-gray-100 bg-gray-10 myCommentTag d-none">내 댓글</span>
{{/if }}
</div>
<div class="d-flex gap-8">
<span class="fs-12 fs-xl-13 fc-gray-70">{{memberOcpnName}} {{career}}년차</span>
<span class="fs-12 fs-xl-13 fc-gray-70">{{convertRegDtime}}</span>
</div>
</div>
</div>
<div class="d-flex order-xl-2 w-xl-100 px-0">
<!-- Input-Group : Start -->
<div class="d-flex order-xl-2 w-xl-100">
<span class="fs-14 fc-gray-100 contentCmt" id="contentCmt-{{contsCmtId}}"
style="white-space: pre-line;">{{content}}</span>
</div>
<!-- Input-Group : End -->
</div>
<div class="d-flex justify-content-end">
{{#if myCommentYn}}
{{> myComment}}
{{/if}}
</div>
</div>
</div>
<!-- 대댓글 박스 : Input-Group : End -->
{{/each }}
</div>
<!-- Reply : End -->
{{/if }}
</div>
{{/each }}
{{#if isEndPage}}
<a class="icon-link" id="showMoreCmtButton">더보기<i class="ico ico-chevron-bottom sz-20"></i></a>
{{/if }}
</script>
<!-- [...]아이콘 댓글 수정/삭제 드롭다운 -->
<script type="text/x-handlebars-template" id="myCommentTemplate">
<!-- DropDown : Start -->
<div class="dropdown">
<a class="dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="ico ico-more-dot sz-32"></i>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item updateCmt" id="updateComment" data-conts-cmt-id="{{contsCmtId}}">댓글 수정</a></li>
<li><a class="dropdown-item deleteCmt" id="deleteCmtButton" data-conts-cmt-id="{{contsCmtId}}">댓글 삭제</a></li>
</ul>
</div>
<!-- DropDown : End -->
</script>
<!-- 대댓글 박스 -->
<script type="text/x-handlebars-template" id="reCommentTextAreaTemplate">
<!-- Input-Group : Start -->
<div class="input-group" id="reCommentTextAreaDiv">
<textarea class="form-control black" placeholder="댓글을 입력하세요." id="contentReCmtTextArea"></textarea>
<button type="button" class="btn btn-secondary h-100" id="reCmtWriteButton">댓글남기기</button>
</div>
<!-- Input-Group : End -->
</script>
<!-- 댓글 : 수정 양식 템플릿 -->
<script type="text/x-handlebars-template" id="updateCommentTemplate">
<div class="input-group" id="updateCmtDiv">
<textarea class="form-control black" placeholder="댓글로 남겨보세요." id="updateCmtText"></textarea>
<button type="button" id="updateCmtButton" class="btn btn-secondary h-100">댓글 수정</button>
</div>
</script>
<script>
// GTM dataLayer: 주간 인기 콘텐츠 여부 + 인기 페이지 유입 여부 (시나리오 4)
window.dataLayer = window.dataLayer || [];
dataLayer.push({
'isWeeklyPopular': false,
'fromPopular': false,
'contentId': '19105',
'contentRank': '0'
});
</script>
<script>
$(function () {
// 1. 로컬변수
const memberId = '0'; // 화면 진입 시 로그인이 되어 있다면 이 값이 들어있음. 이걸로 로그인 체크 로직 대체
const data = {"title":"자체 브랜드 제품개발, 실무에서 바로 쓰는 5단계 프로세스 정리","content":"\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e자기 브랜드 상품을 직접 만들어 파는 셀러가 빠르게 늘고 있어요. 외주 제작 인프라가 잘 갖춰져 있어서 아이디어만 있으면 누구든 도전할 수 있는 환경이 된 거죠.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e그런데 실무로 들어가면 이야기가 달라져요. 어디서부터 손을 대야 할지 감이 안 잡히는 경우가 대부분이에요. 제품개발에는 일정한 단계가 있고, 그 흐름을 먼저 파악해두면 불필요한 시행착오를 줄일 수 있어요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e아이디어 구상부터 실제 판매 개시까지, 초기 브랜드가 알아두면 좋을 제품개발프로세스를 다섯 단계로 정리해봤어요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003chr class\u003d\"border-border-200 border-t-0.5 my-3 mx-1.5\"\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u003cstrong\u003e\u003cbr\u003e\u003c/strong\u003e\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u003cstrong\u003e\u003cspan style\u003d\"font-family:Pretendard;font-size: 24pt;\"\u003e검증 없는 아이디어는 재고로 돌아온다\u003c/span\u003e\u003c/strong\u003e\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e신제품개발의 시작은 기획이 아니라 검증이에요. 시장에 수요가 있는지를 데이터로 먼저 확인하지 않으면, 결국 팔리지 않는 물건만 창고에 쌓이게 돼요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e검증 방법 자체는 복잡하지 않아요. 네이버 데이터랩이나 구글 트렌드에서 관련 키워드 검색량 흐름을 보고, 쿠팡이나 스마트스토어에서 유사 상품의 리뷰 수와 판매 추이를 살펴보면 시장 규모와 경쟁 강도를 가늠할 수 있어요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e여기서 놓치기 쉬운 포인트가 하나 있어요. 경쟁 제품의 별점 1점 리뷰를 분석하는 거예요. 소비자가 느끼는 불편, 기존 제품이 해결하지 못한 부분이 거기에 그대로 나와 있거든요. 이걸 파고들면 신제품의 차별화 방향이 잡혀요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003chr class\u003d\"border-border-200 border-t-0.5 my-3 mx-1.5\"\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u003cstrong\u003e\u003cbr\u003e\u003c/strong\u003e\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u003cstrong\u003e\u003cspan style\u003d\"font-family:Pretendard;font-size: 18pt;\"\u003e제품기획 단계에서 차별점을 먼저 설계해야 하는 이유\u003c/span\u003e\u003c/strong\u003e\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e시장 확인이 끝나면 상품개발 기획으로 넘어가요. 이 단계에서 빠지기 쉬운 함정이 있는데, 어떤 물건을 만들지에만 집중하는 거예요. 중요한 건 경쟁 제품 대비 어떤 차이를 줄 수 있느냐예요. 차별점이 없는 제품은 결국 가격으로만 경쟁하게 되고, 마진이 빠르게 무너져요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e초기 브랜드가 실무에서 자주 활용하는 차별화 방향은 네 가지 정도로 나뉘어요. 디자인에 브랜드 감성을 입히는 방식, 리뷰 불만에서 나온 문제를 스펙으로 해결하는 기능 개선, 원료 산지나 개발 철학을 내세우는 스토리텔링, 세트 구성으로 체감 가치를 높이는 번들링이에요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e이 시점에 간단한 콘셉트 문서를 만들어두면 이후 공장 소통에서 커뮤니케이션 비용이 확 줄어요. 타깃 고객, 핵심 차별점, 희망 판매가, 원가 범위 정도만 한 장으로 정리하면 충분해요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003chr class\u003d\"border-border-200 border-t-0.5 my-3 mx-1.5\"\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u003cstrong\u003e\u003cbr\u003e\u003c/strong\u003e\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u003cstrong\u003e\u003cspan style\u003d\"font-family:Pretendard;font-size: 18pt;\"\u003e시\u003c/span\u003e\u003c/strong\u003e\u003cstrong style\u003d\"font-family:Pretendard;font-size: 12pt;\"\u003e\u003cspan style\u003d\"font-family:Pretendard;font-size: 18pt;\"\u003e제품 제작, 샘플 비교 없이 양산으로 넘어가면 안 되는 이유\u003c/span\u003e\u003c/strong\u003e\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e기획 정리가 끝나면 실물 제작에 들어가요. 일반적인 흐름은 레퍼런스 자료를 공장에 전달하고, 1차 샘플을 받아 스펙과 마감을 확인한 뒤, 피드백을 반영해 2~3차 수정을 거쳐 최종 골드 샘플을 확정하는 순서예요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e실무에서 자주 보이는 실수가 있어요. 첫 샘플이 만족스럽다고 곧바로 양산을 진행하는 건데, 반드시 2~3곳 이상 공장에서 샘플을 받아 비교해야 해요. 같은 조건을 전달해도 공장마다 결과물 품질이 상당히 달라요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e샘플을 검토할 때는 시안과 실물의 색상 오차, 소재의 촉감과 내구성, 포장 상태에서의 배송 파손 위험, 해당 카테고리 필수 인증 여부(KC인증, 식약처 등)를 빠짐없이 체크해야 해요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003chr class\u003d\"border-border-200 border-t-0.5 my-3 mx-1.5\"\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u003cstrong\u003e\u003cbr\u003e\u003c/strong\u003e\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u003cstrong\u003e\u003cspan style\u003d\"font-family:Pretendard;font-size: 18pt;\"\u003e양산에서 초도 물량 조절이 중요한 진짜 이유\u003c/span\u003e\u003c/strong\u003e\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e골드 샘플 확정 후 본격 제품양산에 들어가면, 가장 주의할 부분은 초도 수량이에요. 아직 시장 반응이 검증되지 않은 상태에서 대량 생산을 걸면 현금 흐름이 재고에 묶여요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e현실적인 양산 전략은 단계적으로 가는 거예요. 1차에서는 최소 MOQ로 시장 반응을 확인하고, 2차에서는 판매 데이터에 따라 수량을 조정해요. 안정적인 판매 흐름이 확인되면 그때 물량을 늘려서 단가 협상에 들어가는 게 안전해요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e양산 중에도 품질 관리는 멈추면 안 돼요. 생산 라인 초반 제품을 반드시 직접 확인하고, 불량률 허용 범위는 사전에 계약으로 잡아두는 게 리스크를 줄이는 방법이에요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003chr class\u003d\"border-border-200 border-t-0.5 my-3 mx-1.5\"\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u003cstrong\u003e\u003cbr\u003e\u003c/strong\u003e\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u003cstrong\u003e\u003cspan style\u003d\"font-family:Pretendard;font-size: 18pt;\"\u003e출시 전 세팅을 빠뜨리면 제품이 좋아도 안 팔린다\u003c/span\u003e\u003c/strong\u003e\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e제품이 완성된 것과 판매가 가능한 것은 다른 문제예요. 출시 전에 갖춰야 할 요소가 꽤 있어요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e상표권 출원은 제조에 들어가기 전에 먼저 진행하는 게 안전하고, 상세페이지 기획과 제작, 제품 촬영, 판매 채널 입점(스마트스토어, 쿠팡, 자사몰 등), 초기 마케팅 전략(SNS, 인플루언서, 체험단 등)까지 세팅이 끝나야 실제 판매를 시작할 수 있어요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e특히 상세페이지는 소비자가 제품을 처음 만나는 접점이라 기획에 충분한 시간을 들여야 하고요. 마케팅은 출시 당일이 아니라 2~4주 전부터 사전 노출을 쌓아가는 게 초기 트래픽 확보에 효과적이에요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003chr class\u003d\"border-border-200 border-t-0.5 my-3 mx-1.5\"\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u003cstrong\u003e\u003cbr\u003e\u003c/strong\u003e\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u003cstrong\u003e\u003cspan style\u003d\"font-family:Pretendard;font-size: 18pt;\"\u003e전체 소요 기간\u003c/span\u003e\u003c/strong\u003e\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e아이디어 검증에 1~2주, 제품기획에 1~2주, 시제품 제작에 4~8주, 양산에 2~6주, 출시 준비에 2~3주 정도가 걸려요. 카테고리와 제조 방식에 따라 편차는 있지만, 전체적으로 약 3~5개월이 일반적인 타임라인이에요.\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003chr class\u003d\"border-border-200 border-t-0.5 my-3 mx-1.5\"\u003e \n\u003cp\u003e\u0026nbsp;\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e\u0026nbsp;\u003c/p\u003e \n\u003cp class\u003d\"font-claude-response-body break-words whitespace-normal leading-[1.7]\"\u003e제품개발 전체 흐름을 잡아두면 각 단계에서 어디에 리소스를 집중해야 할지가 분명해져요. 그런데 이 프로세스를 실행에 옮기려면 한 가지 더 결정할 게 있어요. 제조 방식에 따라 비용 구조와 일정, 품질 컨트롤 범위가 완전히 달라지거든요. OEM과 ODM 방식의 차이와 초기 브랜드에 맞는 선택 기준은 다음 글에서 이어서 풀어볼게요.\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e","pubDtime":"20260402112800","thumbFileName":"자체 브랜드 제품개발 실무에서 바로 쓰는 5단계 프로세스 정리.png","thumbFilePath":"https://openads-real.s3.amazonaws.com/openadsAdmin/images/contsThumb/","regDtime":"20260402102851","contsId":19105,"contsSummary":"제품개발 아이디어 검증부터 제품기획, 시제품제작, 양산, 출시까지 5단계 프로세스를 정리했습니다. 초기 브랜드가 자주 하는 실수와 현실적인 타임라인까지 확인해 보세요.\r\n","subCategoryName":"쇼핑몰 창업/운영","drawYn":false,"likeYn":false,"newYn":false,"contsReport":{"contsId":19105,"viewCnt":852,"likeCnt":1,"shareCnt":1},"isNew":false,"categoryCode":"CC92","subCategoryCode":"CC94","categoryName":"비즈니스","magneName":"원포인트 인사이트","authorId":705,"viewCnt":852,"likeCnt":1,"shareCnt":1,"authorName":"원포인트(롸잇)","authorDesc":"탑티어 마케터 파트타임 고용 서비스, 원포인트입니다. 원포인트에서 활동 중인 업계 10% 탑티어 마케터의 다양한 인사이트와 업무 효율화, 프리랜서 시장에 대한 여러 소식을 전합니다","imgFileName":"Frame 1.png","imgFilePath":"https://openads-real.s3.amazonaws.com/openadsAdmin/images/authorImg/","tags":[{"tagName":"제품개발","prior":1,"curnYn":"N","regDtime":"20260402112354","contsId":19105,"contsTagId":176131,"isNew":false},{"tagName":"제품기획","prior":2,"curnYn":"N","regDtime":"20260402112354","contsId":19105,"contsTagId":176132,"isNew":false},{"tagName":"상품개발","prior":3,"curnYn":"N","regDtime":"20260402112354","contsId":19105,"contsTagId":176133,"isNew":false},{"tagName":"제품양산","prior":4,"curnYn":"N","regDtime":"20260402112354","contsId":19105,"contsTagId":176134,"isNew":false},{"tagName":"제품개발프로세스","prior":5,"curnYn":"N","regDtime":"20260402112354","contsId":19105,"contsTagId":176135,"isNew":false}],"likeRatio":5,"viewRatio":5,"shareRatio":10}; // 화면 상세의 데이터를 json 형태로 변환한 값
const contsId = String(ou.getUrlParams().contsId || '').trim();
/* 주제/유형별 추천 콘텐츠 관련 변수 */
let offset = 0;
let currentCnt = 0; //콘텐츠 갯수
/** 카카오 공유하기 모음 **/
let kakaoImgWidth=486;
let kakaoImgHeight=344;
/** 댓글용 변수 모음 */
let commentId;
let cmtMoreBtnCnt = 4; //댓글 5개씩 표현하기 위한 offset
let cachedCommentList = []; // 서버에서 받은 댓글 캐시 (더보기는 재호출 없이 이 캐시에서 렌더)
let existUpdateCmtBox = false; // 댓글 수정 영역 활성화 여부
//2. 로컬함수
/** 외부 스크립트 로드 */
function outsideScriptLoadFn() {
//페북 스크립트
$('#contentDetailContent a').attr('target', '_target');
fbq('track', 'ViewContent', { content_ids : '19105' } );
// FlareLane: 콘텐츠 조회 이벤트
ou.fl.logEvent(ou.fl.keys.event.viewContent, {
content_id: String(data.contsId),
content_title: data.title,
author_id: String(data.authorId),
category: 'CC92',
category_name: '비즈니스',
sub_category: 'CC94',
sub_category_name: '쇼핑몰 창업/운영',
published_at: '20260402'.replace(/(\d{4})(\d{2})(\d{2})/, '$1-$2-$3'),
from_popular: false,
is_weekly_popular: false
});
// FlareLane: 구글 애즈 DA 유입 사용자의 콘텐츠 조회 이벤트
(function() {
const utmSource = (sessionStorage.getItem('fl_utm_source') || '').toLowerCase();
const utmMedium = (sessionStorage.getItem('fl_utm_medium') || '').toLowerCase();
const utmCampaign = sessionStorage.getItem('fl_utm_campaign') || '';
const gclid = sessionStorage.getItem('fl_gclid') || '';
const isGoogleSource = utmSource === 'google' || utmSource === 'googleads' || utmSource === 'google_ads';
const isDaMedium = utmMedium === 'da' || utmMedium === 'display' || utmMedium === 'gdn' || utmMedium === 'google_da';
const isGoogleDaUser = (isGoogleSource && isDaMedium) || (!utmSource && !!gclid) || (isGoogleSource && !!gclid);
if (!isGoogleDaUser) return;
ou.fl.logEvent(ou.fl.keys.event.viewContentGoogleDa, {
content_id: String(data.contsId),
content_title: data.title,
author_id: String(data.authorId),
category: 'CC92',
sub_category: 'CC94',
utm_source: utmSource,
utm_medium: utmMedium,
utm_campaign: utmCampaign,
has_gclid: !!gclid
});
})();
// FlareLane: 특정 작가 글 3회마다 조회 시 미구독 유저에게 구독 제안 인앱메시지
(function() {
const authorId = String(data.authorId);
if (!authorId || authorId === '0') return;
if (false) return;
ou.flTrackViewCount('fl_author_view_' + authorId, 3, function() {
ou.fl.logEvent(ou.fl.keys.event.viewAuthorThird, {
author_id: authorId,
author_name: data.authorName || ''
});
ou.fl.displayInApp(ou.fl.keys.inApp.viewAuthorThird);
});
})();
// FlareLane: 카테고리 3회마다 조회 시 서버에 최신글 푸시 발송 요청
(function() {
const categoryCode = 'CC92';
if (!categoryCode) return;
// FlareLane: 웹푸시 권한이 허용된 기기에서만 발송을 트리거한다.
// 미허용 기기는 FlareLane에 등록된 발송 대상이 아니므로, 호출 시 404 'Contains invalid ID'만 유발한다.
if (!('Notification' in window) || Notification.permission !== 'granted') return;
ou.flTrackViewCount('fl_category_view_' + categoryCode, 3, function() {
ou.flGetDeviceId(function(deviceId) {
console.log('[FlareLane] deviceId:', deviceId, 'userId:', '0');
$.send2('/content/contentDetail/sendCategoryPush', {
categoryCode: categoryCode,
deviceId: deviceId || ''
}, function(json) {});
});
});
})();
// FlareLane: 인앱메시지 — 콘텐츠 조회 트리거 (T-6)
ou.fl.displayInApp(ou.fl.keys.inApp.viewContent);
// FlareLane: 오스토리 등 특정 카테고리 콘텐츠 조회 시 인사이터 페이지 안내 인앱메시지 (노출 조건은 대시보드에서 view_content 이벤트의 category/sub_category 필터로 설정)
ou.fl.displayInApp(ou.fl.keys.inApp.ostoryContentView);
//dable 스크립트
(function(d,a){d[a]=d[a]||function(){(d[a].q=d[a].q||[]).push(arguments)};}(window,'dable'));
dable('renderWidget', 'dablewidget_37JPpyXN');
/* MORE 스크립트 수집 */
try{
var evt_data = {};
evt_data.evt = 'view'; // 이벤트코드 'view' 고정.
evt_data.p_no = `19105`; // url상에서 쓰이는 상품코드
evt_data.p_name = ou.escape(`자체 브랜드 제품개발, 실무에서 바로 쓰는 5단계 프로세스 정리`, "HTML"); // 상품명
evt_data.price = '0'; // 상품판매가격(숫자만 포함한 문자열)
evt_data.regular_price = '0'; // 상품정가(숫자만 포함한 문자열)
evt_data.thumb = `https://openads.co.kr/openadsAdmin/images/자체 브랜드 제품개발 실무에서 바로 쓰는 5단계 프로세스 정리.png`; // 상품이미지 url
evt_data.p_url = `https://www.openads.co.kr/content/contentDetail?contsId=19105`; // 해당 상품페이지 url(트래킹 코드 등이 포함되지 않은 순수 url)
evt_data.cate1 = `비즈니스`; // 카테고리 대분류. 존재하지 않으면 ''
evt_data.cate2 = `쇼핑몰 창업/운영`; // 카테고리 중분류. 존재하지 않으면 ''
evt_data.cate3 = ''; // 카테고리 소분류. 존재하지 않으면 ''
evt_data.soldout = '0'; // 품절여부. (품절이 아닐경우 '0', 품절일 경우 '1')
mcroPushEvent(evt_data);
}catch(e){}
/* MORE 스크립트 수집 끝 */
/* MORE 티커 스크립트 */
var meta = document.createElement('meta');
meta.setAttribute('name', 'more_page_type');
meta.setAttribute('content', 'detail');
document.getElementsByTagName('head')[0].appendChild(meta);
var meta2 = document.createElement('meta');
meta2.setAttribute('name', 'more_detail_pid');
meta2.setAttribute('content', '19105');
document.getElementsByTagName('head')[0].appendChild(meta2);
/* MORE 티커 스크립트 끝 */
//카카오 관련
if(data.thumbFileName == 'CONTS_DEFAULT_IMG.jpg'){
data.thumbFilePath = 'https://openads.co.kr'+data.thumbFilePath;
}
var img = new Image();
img.onload = function(){
kakaoImgWidth = this.width
kakaoImgHeight = this.height;
};
img.src = data.thumbFilePath+data.thumbFileName;
shareKakao();
}
/**
* 지정 영역이 화면(뷰포트)에 들어올 때 loadFn을 1회만 실행한다.
* - 위쪽 200px 미리 로드해서 스크롤 끊김을 줄인다.
* - 영역이 이미 화면 안(상단 노출)이면 즉시 실행되고, IntersectionObserver 미지원 시에도 즉시 실행(안전한 폴백).
*/
const lazyLoadOnVisible = (selector, loadFn) => {
const el = document.querySelector(selector);
if (!el || typeof IntersectionObserver === 'undefined') { loadFn(); return; }
const io = new IntersectionObserver(function (entries, observer) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
observer.unobserve(entry.target);