Local RAG MVP
퍼블리싱 가이드 챗봇
이렇게 검색해 보세요
문장으로 길게 묻기보다 핵심 용어를 넣으면 더 잘 찾습니다. 예: 이미지 alt, 색 대비, 모달 접근성, form label, 배포 체크리스트
질문 예시 모음
컴포넌트 질문
SCSS 질문
검사와 리뷰 질문
코드 검사
HTML 일부를 붙여넣거나 `.html` 파일을 선택해 접근성 기본 항목을 확인합니다.
공개 배포에서는 외부 공개 URL만 검사할 수 있으며, `localhost`와 내부망 주소는 차단됩니다.
배포 전 체크리스트
작업 흐름에 맞춰 기본 구조, 상호작용, 화면 품질, 검사 결과를 차례대로 확인합니다.
작업 시작 전
마크업 기본
폼과 입력
컴포넌트 상호작용
반응형과 화면 품질
검사 결과 재확인
기준 소스
자주 쓰는 HTML, CSS/SCSS, JS 기본 소스를 복사해서 시작점으로 사용할 수 있습니다.
HTML 기준
기본 HTML 구조
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>페이지 제목</title>
</head>
<body>
<header>...</header>
<main id="main">...</main>
<footer>...</footer>
</body>
</html>
접근 가능한 버튼
<button type="button">저장</button>
<button type="button" aria-label="닫기">
<span aria-hidden="true">×</span>
</button>
폼 label 연결
<label for="email">이메일</label>
<input id="email" name="email" type="email" autocomplete="email">
<p id="email-error" class="error-message">이메일 형식을 확인해 주세요.</p>
<input aria-describedby="email-error">
이미지 alt
<img src="event-banner.jpg" alt="여름 할인 이벤트 안내">
<img src="decor-line.svg" alt="">
CSS/SCSS 기준
스킵 링크와 sr-only
<a class="skip-link" href="#main">본문 바로가기</a>
.skip-link {
position: absolute;
left: 16px;
top: -48px;
}
.skip-link:focus {
top: 16px;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
}
컴포넌트 상태 클래스
.accordion {
border: 1px solid #dfe3e6;
}
.accordion.is-open .accordion__panel {
display: block;
}
.accordion__button:focus-visible {
outline: 3px solid #1a73e8;
outline-offset: 2px;
}
SCSS 변수와 breakpoint
$breakpoint-tablet: 768px;
$breakpoint-desktop: 1024px;
$color-focus: #1a73e8;
@mixin tablet {
@media (min-width: $breakpoint-tablet) {
@content;
}
}
.card {
padding: 16px;
@include tablet {
padding: 24px;
}
}
SCSS 폴더 구조
scss/
abstracts/
_variables.scss
_tokens.scss
_mixins.scss
_z-index.scss
base/
_font.scss
_root.scss
_reset.scss
_accessibility.scss
layout/
_common-inner.scss
_header.scss
_footer.scss
components/
_button.scss
_form.scss
_modal.scss
_popup.scss
_table.scss
templates/
_template-base.scss
_image-template.scss
_text-template.scss
_sub-page.scss
sections/
_visual.scss
_info.scss
_directions.scss
editor/
_design-edit-page.scss
_selection.scss
_design-util.scss
style.scss
SCSS 공통 mixin
$breakpoint-pc: 1024px;
@mixin pc {
@media screen and (min-width: $breakpoint-pc) {
@content;
}
}
@mixin public-mode {
html:not(.design-edit-page) & {
@content;
}
}
@mixin public-pc {
@include pc {
html:not(.design-edit-page) & {
@content;
}
}
}
@mixin editor-mode {
html.design-edit-page & {
@content;
}
}
SCSS mixin 모음
@mixin font-style(
$size,
$weight: 400,
$line-height: 1.5,
$letter-spacing: 0,
$color: null
) {
font-size: $size;
font-weight: $weight;
line-height: $line-height;
letter-spacing: $letter-spacing;
@if $color != null {
color: $color;
}
}
@mixin position($position: absolute, $top: null, $right: null, $bottom: null, $left: null) {
position: $position;
@if $top != null { top: $top; }
@if $right != null { right: $right; }
@if $bottom != null { bottom: $bottom; }
@if $left != null { left: $left; }
}
@mixin center-x($position: absolute) {
position: $position;
left: 50%;
transform: translateX(-50%);
}
@mixin center-y($position: absolute) {
position: $position;
top: 50%;
transform: translateY(-50%);
}
@mixin center($position: absolute) {
position: $position;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
@mixin flex($direction: null, $justify: null, $align: null, $gap: null) {
display: flex;
@if $direction != null { flex-direction: $direction; }
@if $justify != null { justify-content: $justify; }
@if $align != null { align-items: $align; }
@if $gap != null { gap: $gap; }
}
@mixin bg-image($url, $size: cover, $position: center) {
background-image: url($url);
background-repeat: no-repeat;
background-position: $position;
background-size: $size;
}
@mixin reset-list {
margin: 0;
padding: 0;
list-style: none;
}
@mixin ellipsis($lines: 1) {
overflow: hidden;
text-overflow: ellipsis;
@if $lines == 1 {
white-space: nowrap;
} @else {
display: -webkit-box;
-webkit-line-clamp: $lines;
-webkit-box-orient: vertical;
}
}
@mixin size($width, $height: $width) {
width: $width;
height: $height;
}
SCSS 실무 mixin 예시
@use 'sass:math';
@function strip-unit($value) {
@if type-of($value) == 'number' and not unitless($value) {
@return math.div($value, $value * 0 + 1);
}
@return $value;
}
$breakpoints: (
mobile: 480px,
tablet: 768px,
desktop: 1024px,
wide: 1280px
);
@mixin respond-to($name) {
$width: map-get($breakpoints, $name);
@if $width == null {
@error 'Unknown breakpoint: #{$name}';
}
@media (min-width: $width) {
@content;
}
}
@mixin fluid-type($min-size, $max-size, $min-vw: 360px, $max-vw: 1200px) {
font-size: clamp(
$min-size,
calc(#{$min-size} + (#{strip-unit($max-size)} - #{strip-unit($min-size)}) * ((100vw - #{$min-vw}) / (#{strip-unit($max-vw)} - #{strip-unit($min-vw)}))),
$max-size
);
}
@mixin aspect-ratio-box($width, $height) {
aspect-ratio: #{$width} / #{$height};
@supports not (aspect-ratio: 1 / 1) {
position: relative;
&::before {
content: '';
display: block;
padding-top: calc($height / $width * 100%);
}
> * {
position: absolute;
inset: 0;
}
}
}
@mixin absolute-fill {
position: absolute;
inset: 0;
}
@mixin hover-supported {
@media (hover: hover) and (pointer: fine) {
@content;
}
}
@mixin reduced-motion {
@media (prefers-reduced-motion: reduce) {
@content;
}
}
@mixin touch-target($size: 44px) {
min-width: $size;
min-height: $size;
}
SCSS 접근성 유틸
$color-focus: #1a73e8;
@mixin focus-ring {
outline: 3px solid $color-focus;
outline-offset: 2px;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
}
.skip-link {
position: absolute;
left: 16px;
top: -48px;
z-index: 1000;
&:focus {
top: 16px;
@include focus-ring;
}
}
SCSS 컴포넌트 예시
.accordion {
border: 1px solid $color-border;
&__button {
width: 100%;
min-height: 44px;
text-align: left;
&:focus-visible {
@include focus-ring;
}
}
&__panel {
padding: 16px;
}
&:not(.is-open) &__panel {
display: none;
}
&.is-open &__button {
font-weight: 700;
}
}
SCSS style.scss 조립
@use 'abstracts/variables';
@use 'abstracts/tokens';
@use 'abstracts/mixins';
@use 'base/font';
@use 'base/root';
@use 'base/reset';
@use 'base/accessibility';
@use 'layout/common-inner';
@use 'layout/header';
@use 'layout/footer';
@use 'components/button';
@use 'components/form';
@use 'components/modal';
@use 'components/popup';
@use 'templates/template-base';
@use 'sections/visual';
@use 'sections/info';
@use 'editor/design-edit-page';
JS 기준
모달 포커스 복귀
const openButton = document.querySelector('[data-modal-open]');
const closeButton = document.querySelector('[data-modal-close]');
const modal = document.querySelector('[data-modal]');
let lastFocusedElement = null;
function openModal() {
lastFocusedElement = document.activeElement;
modal.hidden = false;
closeButton.focus();
}
function closeModal() {
modal.hidden = true;
lastFocusedElement?.focus();
}
openButton.addEventListener('click', openModal);
closeButton.addEventListener('click', closeModal);
aria-expanded 토글
const button = document.querySelector('[data-toggle-button]');
const panel = document.querySelector('[data-toggle-panel]');
button.addEventListener('click', () => {
const isOpen = button.getAttribute('aria-expanded') === 'true';
button.setAttribute('aria-expanded', String(!isOpen));
panel.hidden = isOpen;
});
사용법
실행 방법
프로젝트 폴더로 이동한 뒤 서버를 실행합니다.
cd /path/to/publishing-guide-chatbot
npm start
포트를 바꿔야 하면 PORT=8080 npm start처럼 실행합니다. 로컬에서는 http://localhost:3000, 배포 후에는 https://chatbot.biny.cloud로 접속합니다.
작업 흐름
- 작업 시작 전에는 체크리스트 탭에서 확인할 기준을 먼저 봅니다.
- 구현 중 헷갈리는 규칙은 가이드 질문 탭에서 짧은 핵심어로 검색합니다.
- 마크업을 작성한 뒤 코드 검사 탭에 HTML을 붙여넣거나 파일을 선택합니다.
- 검사 결과의 가이드 보기 버튼으로 관련 규칙을 다시 확인합니다.
- 마무리 전에 체크리스트 탭으로 돌아와 누락된 항목을 점검합니다.
검색 팁
긴 문장보다 핵심어가 잘 잡힙니다.
이미지 alt색 대비모달 접근성form label폼 오류 메시지button link 구분배포 전 접근성 체크리스트
챗봇 질문 예시
아래처럼 핵심어와 목적을 함께 넣으면 답을 더 잘 찾습니다.
SCSS 파일은 어떻게 나누면 좋아?모달 접근성에서 확인할 것은 뭐야?드롭다운은 어떻게 마크업해?검사 결과는 어떻게 다시 확인해?회사 CSS 분석 내용 보여줘
검색이 안 될 때
질문을 더 짧게 바꾸거나, 문서에 해당 내용이 있는지 확인합니다. 표현만 다른 경우에는 src/search.js의 동의어 목록에 추가하면 됩니다.
문서 추가 위치
챗봇 답변은 docs/guide/ 안의 Markdown 문서에서 나옵니다. 프로젝트 규칙이 생기면 이 폴더의 문서를 먼저 수정하세요.
배포 확인
배포 후에는 /health 응답, 메인 화면 접속, 가이드 질문, HTML 검사 API를 순서대로 점검합니다. 자세한 절차는 docs/DEPLOYMENT.md를 참고합니다.