Http Client |
---|
NodeJS | |
---|---|
NPM |
Netlify |
---|
Style & UI Library |
---|
Remix | |
---|---|
server state | |
Forms |
타이트한 일정으로 진행하게되었던 프로젝트라 일단 static Mock data로 모든 파트를 구성했습니다. 여유가 되는대로 어떤 기술스택을 도입할 것인지 차차 고민해볼 예정입니다.
(예정 기술스택 )
DMBS | |
---|---|
ORM |
.
├── asset
│ └── icon <-- svg 컴포넌트를 관리합니다.
│ ├── ArrowRight.jsx
│ ├── CheckIcon.jsx
│ ├── CouponClose.jsx
│ ├── ErrorIcon.jsx
│ ├── Garbage.jsx
│ ├── Kakao.jsx
│ ├── Logo.jsx
│ ├── Naver.jsx
│ ├── ProblemArrow.jsx
│ └── procedure
│ ├── Arrow.jsx
│ └── BranchArrow.jsx
├── components <-- 공통 컴포넌트를 관리합니다.
│ ├── Footer
│ │ ├── CompanyInfo.jsx
│ │ ├── Mobile
│ │ │ └── MobileCompanyInfo.jsx
│ │ ├── SNSGroup.jsx
│ │ └── index.jsx
│ ├── Header
│ │ ├── MobileNavItems.jsx
│ │ ├── Nav.jsx
│ │ ├── NavItems.jsx
│ │ └── constants
│ │ └── navData.js
│ ├── NotFoundPage.jsx
│ ├── common
│ │ ├── AccordionSection.jsx
│ │ ├── CommonFooterSection.jsx
│ │ ├── ConsultationCard.jsx
│ │ ├── CustomMantine
│ │ │ └── MultiLineText.jsx
│ │ ├── DefaultButton.jsx
│ │ ├── FeatureList.jsx
│ │ ├── Layout.jsx
│ │ ├── MiddleBanner.jsx
│ │ ├── PriceInfo
│ │ │ ├── Mobile
│ │ │ │ └── MobilePriceInfoList.jsx
│ │ │ ├── OtherItem.jsx
│ │ │ ├── PriceInfo.jsx
│ │ │ ├── PriceInfoList.jsx
│ │ │ └── PriceInfoListSection.jsx
│ │ └── Title.jsx
│ ├── error
│ │ ├── ErrorHandler.jsx
│ │ └── styles
│ │ └── errorHandlerStyles.tsx
│ └── page <-- 라 컴포넌트를 관리합니다.
│ ├── Blog
│ │ ├── BlogCard.jsx
│ │ ├── BlogFilter.jsx
│ │ └── BlogList.jsx
│ ├── FAQ
│ │ ├── FAQAccordionSection.jsx
│ │ └── FAQConsultationSection.jsx
│ ├── Introduce
│ │ ├── IntroduceBanner.jsx
│ │ ├── IntroduceSection.jsx
│ │ ├── LawerProfile.jsx
│ │ └── Problem
│ │ ├── ProblemCard.jsx
│ │ └── ProblemSection.jsx
│ ├── Main
│ │ ├── ConsultationForm.jsx
│ │ ├── CouponSticker.jsx
│ │ ├── FormBox.jsx
│ │ ├── InfiniteVerticalCarousel.jsx
│ │ ├── MainCard.jsx
│ │ ├── MainCardSection.jsx
│ │ ├── MainFirstSection.jsx
│ │ ├── MainHeader.jsx
│ │ ├── MainLargeCard.jsx
│ │ ├── MainThirdSection.jsx
│ │ └── utils
│ │ └── validation.server.js
│ ├── Pricing
│ │ ├── EventSection.jsx
│ │ └── FeaturedFAQSection.jsx
│ └── Process
│ ├── KakaoSection.jsx
│ ├── LitigationInfo.jsx
│ ├── Procedure
│ │ ├── Consultation.jsx
│ │ ├── Desktop
│ │ │ └── ProcedureList.jsx
│ │ ├── LitigationProcedure.jsx
│ │ ├── LitigationProcedureTitle.jsx
│ │ ├── Mobile
│ │ │ ├── MobileProcedure.jsx
│ │ │ └── MobileProcedureList.jsx
│ │ ├── Procedure.jsx
│ │ └── Sticker
│ │ ├── Sticker.jsx
│ │ └── StickerList.jsx
│ └── ProcessConsultationSection.jsx
├── constants <-- 상수폴더, 목데이터들을 관리합니다.
│ ├── NavData.jsx
│ ├── common
│ │ ├── featuredListData.jsx
│ │ └── priceInfoData.jsx
│ ├── page
│ │ ├── blogPageData.jsx
│ │ ├── faqPageData.jsx
│ │ ├── introducePageData.jsx
│ │ ├── mainPageData.jsx
│ │ ├── pricingPageData.jsx
│ │ └── processPageData.jsx
│ ├── rootAsset.js
│ └── theme.jsx
├── entry.client.jsx <-- 브라우저 번들의 진입점입니다. entry.server.tsx에서 생성된 마크업을 ReHydrate하는데 사용됩니다.
├── entry.server.jsx <-- SSR이 이루어지는 진입점입니다. 기본적인 마크업, HTTP Response를 생성하는데 사용됩니다.
├── hooks <-- 공용 커스텀 훅을 관리합니다.
│ ├── useAddKakaoChannel.jsx
│ ├── useInterval.jsx
│ └── useResponsive.jsx
├── root.jsx
├── routes <— 라우터의 집합소입니다. 해당 라우터의 action / loader / Error등 서버상태를 관리하며 뼈대의 역할을 합니다.
│ ├── [robots.txt].jsx <— SEO를 위한 파일 역시 routes에서 관리합니다.
│ ├── [sitemap.xml].jsx <— SEO를 위한 파일 역시 routes에서 관리합니다.
│ ├── index.jsx
│ ├── md
│ │ ├── blog.jsx
│ │ ├── faq.jsx
│ │ ├── index.jsx
│ │ ├── introduce.jsx
│ │ ├── pricing.jsx
│ │ └── process.jsx
│ └── md.jsx
├── services
├── styles <-- 스타일을 관리합니다.
│ ├── components <-- 공용 컴포넌트의 스타일을 관리합니다.
│ │ ├── PriceInfo
│ │ │ ├── useOtherItemStyles.jsx
│ │ │ ├── usePriceInfoListStyles.jsx
│ │ │ └── usePriceInfoStyles.jsx
│ │ ├── useAccordianStyles.jsx
│ │ ├── useCommonFooterSectionStyles.jsx
│ │ ├── useConsultationCardStyles.jsx
│ │ ├── useDefaultButtonStyles.jsx
│ │ ├── useFeatureListStyles.jsx
│ │ ├── useFooterStyles.jsx
│ │ ├── useMiddleBannerStyles.jsx
│ │ ├── useNavStyles.jsx
│ │ └── useStickerStyles.jsx
│ ├── page <-- 라우터 컴포넌트의 스타일을 관리합니다.
│ │ ├── Blog
│ │ │ ├── useBlogCardStyles.jsx
│ │ │ ├── useBlogFilterStyles.jsx
│ │ │ └── useBlogListStyles.jsx
│ │ ├── FAQ
│ │ ├── Introduce
│ │ │ ├── Problem
│ │ │ │ └── useProblemCardStyles.jsx
│ │ │ ├── useIntroduceBannerStyles.jsx
│ │ │ ├── useIntroduceSectionStyles.jsx
│ │ │ └── useLwaerProfileStyles.jsx
│ │ ├── Main
│ │ │ ├── useConsultationFormStyles.jsx
│ │ │ ├── useCouponStickerStyles.jsx
│ │ │ ├── useFormBoxStyles.jsx
│ │ │ ├── useMainCardSectionStyles.jsx
│ │ │ ├── useMainCardStyles.jsx
│ │ │ ├── useMainHeaderStyles.jsx
│ │ │ ├── useThirdSectionStyles.jsx
│ │ │ └── useVerticalCarouselStyles.jsx
│ │ ├── Pricing
│ │ │ ├── useEventSectionStyles.jsx
│ │ │ ├── useFeaturedFAQStyles.jsx
│ │ │ └── usePricingStyles.jsx
│ │ ├── Process
│ │ │ ├── Mobile
│ │ │ │ └── useMobileProcedureStyles.jsx
│ │ │ ├── useKakaoSectionStyles.jsx
│ │ │ ├── useLitigationInfoStyles.jsx
│ │ │ ├── useLitigationProcedureTitleStyles.jsx
│ │ │ └── useProcedureStyles.jsx
│ │ └── useNotFoundPageStyles.jsx
│ └── reset.css
├── utils <-- 서버 상태 핸들링 로직을 관리합니다.
│ └── actionHandler.js
└── utils.js
모든 write request는
태그, 혹은 useSubmit() API로 이루어집니다.모든 validation 로직은 서버에서 처리합니다.
일관성 있는 로직 처리는 책임분리를 명확히하여
유지보수성을 증가시킵니다.
export async function action({ request }) {
const formData = await request.formData();
const { ...fields } = Object.fromEntries(formData);
const { name, phoneNumber } = fields;
const fieldErrors = {
name: validateName(name),
phoneNumber: validatePhoneNumber(phoneNumber),
};
const arrayedObj = Object.values(fieldErrors);
if (arrayedObj.some(Boolean)) {
return badRequest({ fieldErrors, fields });
}
await axios.post(
'https://api.emailjs.com/api/v1.0/email/send',
{
service_id: process.env.EMAIL_JS_SERVICE_ID,
template_id: process.env.EMAIL_JS_TEMPLETE_ID,
user_id: process.env.EMAIL_JS_PUBLIC_KEY,
template_params: { name: 'name', phoneNumber: 'phoneNumber' },
accessToken: process.env.EMAIL_JS_PRIVATE_KEY,
},
{ headers: { 'X-Requested-With': 'XMLHttpRequest' }, withCredentials: true }
);
return redirect('/');
// return redirect('/md/home');
}