최신 웹사이트의 복잡성은 지난 몇 년 동안 크게 증가했습니다. 고품질의 업계 표준 디자인에 대한 수요가 증가함에 따라 프런트엔드 개발자가 직면한 과제가 더욱 가중됩니다.
오늘날 프런트엔드 앱에도 개발 프로세스를 간소화하려면 몇 가지 아키텍처 고려 사항이 필요합니다. 이전 기사 에서는 사이드 프로젝트 를 진행하면서 프런트 엔드 애플리케이션에서 클린 아키텍처 접근 방식을 구현한 경험을 공유했습니다.
이 기사에서 나는 동일한 프로젝트에 대한 나의 경험을 바탕으로 원자 설계 접근 방식을 더 깊이 탐구하는 것을 목표로 합니다. 장점과 단점을 논의하고 다양한 시나리오에서 유용성을 평가하겠습니다.
먼저 디자인 시스템의 개념을 살펴보겠습니다. 디자인 시스템은 팀이 여러 플랫폼에서 일관된 사용자 인터페이스를 디자인하고 개발할 수 있도록 지원하는 재사용 가능한 구성 요소, 지침 및 원칙의 포괄적인 모음입니다.
이는 디자이너와 개발자 모두에게 단일 정보 소스 역할을 하여 제품의 시각적, 기능적 측면이 확립된 브랜드 아이덴티티에 부합하고 이를 준수하도록 보장합니다. 디자인 시스템 구현의 예를 탐색하는 데 관심이 있다면 다음을 검토해 보세요.
디자인 시스템 주제에 대해 더 깊이 알고 싶다면 이 기사를 확인해 보시기 바랍니다. 이 작업의 범위에서 우리에게 필요하지 않은 세부 사항인 이 주제에 대해 자세히 설명합니다.
설계 시스템의 기초를 바탕으로 하는 원자 설계는 재사용 가능한 구성 요소와 지침의 구성과 구조를 간소화하는 방법론입니다. 브래드 프로스트(Brad Frost)가 고안한 Atomic Design은 사용자 인터페이스를 가장 기본적인 빌딩 블록으로 분해하고 이를 보다 복잡한 구조로 재조립하므로 화학에서 영감을 얻었습니다.
다음은 화학에 대한 비유를 보여주는 이미지입니다.
화학 반응은 원자 원소가 어떻게 결합하여 분자를 형성하는지 보여주는 화학 반응식으로 표현됩니다. 위의 예에서 우리는 수소와 산소가 어떻게 결합하여 물 분자를 형성하는지 볼 수 있습니다.
본질적으로 원자 설계는 유연하고 확장 가능한 구성 요소를 만들기 위한 체계적인 접근 방식을 제공하는 설계 시스템의 자연스러운 진화입니다. 원자 설계 원칙을 적용함으로써 팀은 설계 시스템을 보다 효율적으로 관리할 수 있습니다. 이 방법론의 모듈식 특성으로 인해 시스템 내의 구성 요소와 패턴을 더 쉽게 유지 관리, 업데이트 및 확장할 수 있기 때문입니다.
이것이 복잡하게 들릴까 걱정되더라도 걱정하지 마십시오. 다음 섹션에서는 제가 개발한 앱의 실제 사례를 사용하여 이러한 원칙을 적용하는 방법을 보여드리겠습니다. 이를 통해 자신의 프로젝트에서 쉽게 이해하고 구현할 수 있습니다.
원자 설계는 구성 요소를 5가지 개별 수준으로 구성하며 각 수준은 이전 수준을 기반으로 합니다. 이 다섯 가지 수준을 자세히 살펴보겠습니다.
원자 : 사용자 인터페이스의 가장 기본적인 구성 요소인 원자는 버튼, 입력 필드 및 제목과 같은 개별 HTML 요소를 나타냅니다. 이는 가장 작은 기능 단위이므로 더 이상 나눌 수 없습니다.
분자 : 분자는 두 개 이상의 원자를 작용기로 결합하여 형성됩니다. 예를 들어 검색 양식 분자는 검색 입력 원자, 버튼 원자 및 레이블 원자로 구성될 수 있습니다. 분자는 프로젝트 전체에서 재사용할 수 있는 간단한 구성 요소를 나타냅니다.
유기체 : 유기체는 여러 분자 및/또는 원자를 결합하여 생성된 보다 복잡한 구성 요소입니다. 헤더, 바닥글 또는 사이드바와 같은 사용자 인터페이스의 개별 섹션을 나타냅니다. 유기체는 페이지의 전체 레이아웃과 구조를 형성하는 데 도움을 줍니다.
템플릿 : 템플릿은 본질적으로 유기체, 분자 및 원자를 사용하여 구축된 페이지 레이아웃입니다. 실제 콘텐츠를 지정하지 않고 페이지에서 구성 요소의 구조와 배열을 정의하여 다양한 콘텐츠 시나리오에 대한 청사진 역할을 합니다.
페이지 : 페이지는 실제 콘텐츠와 데이터로 완성된 완전히 구현된 최종 템플릿 인스턴스입니다. 이는 사용자가 궁극적으로 보고 상호 작용할 내용을 나타내며 구성 요소와 레이아웃이 다양한 콘텐츠 유형 및 사용 사례에 어떻게 적응하는지 보여줍니다.
프론트엔드 개발을 위한 원자 설계에 대한 박식한 관점을 개발하기 위해 저는 애플리케이션을 만드는 여정을 시작했습니다. 저는 6개월이라는 기간 동안 이 프로젝트를 진행하면서 귀중한 통찰력과 경험을 얻었습니다.
결과적으로, 이 기사 전반에 걸쳐 제공된 예제는 애플리케이션에 대한 실제 경험을 바탕으로 작성되었습니다. 투명성을 유지하기 위해 모든 예제는 공개적으로 액세스 가능한 코드에서 파생되었습니다.
저장소 나 웹사이트 자체를 방문하여 최종 결과를 탐색할 수 있습니다.
명심하세요. 저는 React 로 코딩된 예제를 사용할 것입니다. 이 언어에 익숙하지 않더라도 걱정하지 마세요. 저는 코드의 핵심적인 세부 사항에 초점을 맞추기보다는 원자 설계의 기본 개념을 설명하는 것을 목표로 삼았습니다.
내 저장소의 구성 요소를 더 잘 이해하려면 /client/presentation
디렉터리에서 해당 구성 요소를 찾을 수 있습니다. 이 위치에서는 원자 설계 방법론과 일관된 이름을 유지하기 위해 atoms
이라는 새 디렉터리를 만들었습니다. 이 새 디렉터리에는 전체 온보딩 프로세스를 구축하는 데 필요한 모든 작은 조각이 포함되어 있습니다.
전체 원자 목록은 다음과 같습니다.
atoms ├── box ├── button ├── card ├── card-body ├── card-footer ├── container ├── divider ├── flex ├── form-control ├── form-error-message ├── form-helper-text ├── form-label ├── heading ├── icon ├── input ├── list ├── list-icon ├── list-item ├── spinner ├── tab ├── tab-list ├── tab-panel ├── tab-panels ├── tabs └── text
이러한 원자 이름은 Chakra UI 패키지를 기반으로 하기 때문에 익숙할 수 있습니다. 대부분은 이미 내 애플리케이션에 대한 기본 일치 스타일을 포함하고 있으므로 이 수준에서 특별히 설명할 고유한 항목은 없습니다. 이를 염두에 두고 molecules
대한 논의를 직접 진행할 수 있습니다.
이 단계에서는 원자 설계 과정이 더욱 흥미로워지고 그 진정한 힘이 드러나기 시작합니다. 기본 원자를 정의하는 것은 시간이 많이 걸리고 단조로운 작업이었을 수 있지만 원자를 사용하여 새로운 구성 요소를 만드는 것이 훨씬 더 즐겁습니다.
분자를 정의하기 위해 /client/presentation
디렉토리 내에 molecules
디렉토리를 만들었습니다. 필요한 분자의 전체 목록은 다음과 같습니다.
molecules ├── available-notion-database ├── full-screen-loader ├── input-control ├── onboarding-step-layout └── onboarding-tab-list
실제로 단 5개의 분자만으로도 목표를 달성하기에 충분한 구성 요소를 갖출 수 있습니다. 이는 또한 다른 Atom을 기반으로 구축된 공유 레이아웃을 포함하기에 이상적인 장소라는 점에 유의하는 것이 중요합니다. 예를 들어 onboarding-step-layout
온보딩 프로세스의 5단계 전체에서 일관된 모양을 유지하는 데 활용됩니다.
기타 구성요소는 다음과 같습니다.
available-notion-database : 가져온 사용자의 데이터베이스 세부 정보를 표시하는 데 사용됩니다(사용자는 여러 데이터베이스를 보유할 수 있으므로 4단계에서 하나를 선택할 수 있는 기능을 제공합니다).
구성요소는 UI에 다음과 같이 표시됩니다.
import { FC } from 'react'; import { Flex, Spinner } from '@presentation/atoms'; import { FullScreenLoaderProps } from './full-screen-loader.types'; export const FullScreenLoader: FC<FullScreenLoaderProps> = ({ children, ...restProps }): JSX.Element => ( <Flex alignItems="center" bg="gray.50" height="full" justifyContent="center" left={0} position="fixed" top={0} width="full" zIndex="9999" {...restProps} > <Spinner /> {children} </Flex> );
여기에는 로켓 과학이 없습니다. 이는 이미 정의된 flex
원자와 spinner
원자의 조합일 뿐입니다.
form-label
, form-control
, form-error-label
및 spinner
포함 input
원자에 대한 래퍼로 일부 백그라운드 작업이 발생하는지 표시합니다. 구성요소는 UI에 다음과 같이 표시됩니다.
이제 더 많은 조각이 준비되었으므로 디자인 퍼즐에서 더 큰 블록을 정의하는 작업으로 넘어갈 수 있습니다.
이 섹션에서는 온보딩 프로세스의 각 단계를 표시하는 각 구성 요소를 생성합니다.
명확하게 설명하기 위해 생성된 유기체 목록을 보여드리겠습니다.
organisms ├── onboarding-step-one ├── onboarding-step-two ├── onboarding-step-three ├── onboarding-step-four └── onboarding-step-five
나는 그 이름이 설명이 필요하므로 오해가 있어서는 안 된다고 생각합니다. 모든 것을 하나로 묶는 방법을 설명하기 위해 한 단계의 코드를 예로 제시하겠습니다. 물론 더 많은 내용을 확인하려면 내 저장소를 방문하세요.
export const OnboardingStepFour: FC<OnboardingStepFourProps> = ({ onBackButtonClick, onNextButtonClick, }): JSX.Element => { const { hasApiTokenData, isSetApiTokenLoading, setApiToken, setApiTokenError } = useSetApiToken(); const handleInputChange = debounce(async (event: ChangeEvent<HTMLInputElement>) => { const result = await setApiToken(event.target.value); if (result) { onNextButtonClick(); } }, 1000); return ( <OnboardingStepLayout subtitle="Paste your copied integration token below to validate your integration." title="Validate your integration" onBackButtonClick={onBackButtonClick} > <InputControl isRequired errorMessage={setApiTokenError || undefined} isDisabled={isSetApiTokenLoading || hasApiTokenData} isLoading={isSetApiTokenLoading} label="Integration token" name="integrationToken" placeholder="Your integration token" onChange={handleInputChange} /> </OnboardingStepLayout> ); };
이 코드는 온보딩 프로세스의 4단계를 표시하는 데 전적으로 책임이 있습니다. 나는 당신이 가질 수 있는 유일한 관심사는 유기체에 대한 요청에 관한 것이라고 생각합니다. 이것이 허용됩니까? 모든 경우에 적용되는 일률적인 답변은 없으며 이러한 우려 사항에 대해서는 '상황에 따라 다름'으로 답변해야 합니다. 그것은 귀하의 구조에 따라 다릅니다.
분자나 유기체 내에 API 호출을 포함시키는 것이 애플리케이션의 맥락에서 합리적이고 구성 요소를 지나치게 복잡하게 만들지 않는다면 이는 허용 가능한 솔루션이 될 수 있습니다. 프레젠테이션 구성 요소가 데이터 가져오기 또는 비즈니스 논리와 너무 긴밀하게 결합되지 않도록 주의하세요. 유지 관리 및 테스트가 더 어려워질 수 있기 때문입니다.
내 시나리오에서 이 구성 요소는 한 곳에서 사용되며 해당 시나리오에서 API 호출을 수행하기 위한 다른 솔루션은 더 복잡하고 필요한 것보다 훨씬 더 많은 코드를 생성할 수 있습니다.
이 단계에서는 UI의 세부적인 세부 사항보다는 구성 요소의 구조와 배열에 중점을 둡니다. 또한 템플릿은 일반적으로 템플릿을 사용하는 페이지 구성 요소에 상태 관리가 있어야 하는 위치를 식별하는 데 도움이 됩니다.
제공된 코드 예제에는 템플릿 역할을 하는 Onboarding
구성 요소가 있습니다.
import { FC } from 'react'; import { Flex, Heading, TabPanels, Tabs, Text } from '@presentation/atoms'; import { OnboardingTabList } from '@presentation/molecules'; import { OnboardingStepFive, OnboardingStepFour, OnboardingStepOne, OnboardingStepThree, OnboardingStepTwo, } from '@presentation/organisms'; import { OnboardingProps } from './onboarding.types'; export const Onboarding: FC<OnboardingProps> = ({ activeTabs, createNotionIntegrationTabRef, displayCreateNotionIntegrationTab, displaySelectNotionDatabaseTab, displayShareDatabaseIntegrationTab, displayValidateIntegrationTab, displayVerifyDatabaseTab, selectNotionDatabaseTabRef, shareDatabaseIntegrationTabRef, validateIntegrationTabRef, verifyDatabaseTabRef, }) => ( <Flex direction="column" overflowX="hidden" px={2} py={{ base: '20px', sm: '25px', md: '55px' }}> <Flex direction="column" textAlign="center"> <Heading color="gray.700" fontSize={{ base: 'xl', sm: '2xl', md: '3xl', lg: '4xl' }} fontWeight="bold" mb="8px" > Configure your Notion integration </Heading> <Text withBalancer color="gray.400" fontWeight="normal"> This information will let us know from which Notion database we should use to get your vocabulary. </Text> </Flex> <Tabs isLazy display="flex" flexDirection="column" mt={{ base: '10px', sm: '25px', md: '35px' }} variant="unstyled" > <OnboardingTabList activeTabs={activeTabs} createNotionIntegrationTabRef={createNotionIntegrationTabRef} selectNotionDatabaseTabRef={selectNotionDatabaseTabRef} shareDatabaseIntegrationTabRef={shareDatabaseIntegrationTabRef} validateIntegrationTabRef={validateIntegrationTabRef} verifyDatabaseTabRef={verifyDatabaseTabRef} /> <TabPanels maxW={{ md: '90%', lg: '100%' }} mt={{ base: '10px', md: '24px' }} mx="auto"> <OnboardingStepOne onNextButtonClick={displayCreateNotionIntegrationTab} /> <OnboardingStepTwo onBackButtonClick={displayVerifyDatabaseTab} onNextButtonClick={displayShareDatabaseIntegrationTab} /> <OnboardingStepThree onBackButtonClick={displayCreateNotionIntegrationTab} onNextButtonClick={displayValidateIntegrationTab} /> {activeTabs.validateIntegration ? ( <OnboardingStepFour onBackButtonClick={displayShareDatabaseIntegrationTab} onNextButtonClick={displaySelectNotionDatabaseTab} /> ) : null} {activeTabs.selectNotionDatabase ? ( <OnboardingStepFive onBackButtonClick={displayVerifyDatabaseTab} /> ) : null} </TabPanels> </Tabs> </Flex> );
이 Onboarding
구성요소는 원자, 분자 및 유기체를 조립하여 온보딩 프로세스를 위한 레이아웃을 만듭니다. 상태 관리 및 탭 탐색 논리가 이 구성 요소에서 분리되었습니다. 이제 필요한 상태 및 콜백 함수가 props로 수신되므로 더 높은 수준의 "페이지" 구성 요소가 상태 및 데이터 관리를 처리할 수 있습니다.
이러한 관심사 분리는 템플릿이 레이아웃과 구조에 집중하는 동시에 상태 관리가 적절한 수준에서 처리되도록 보장합니다.
마지막으로 4단계를 최종 결과로 제시하고 싶습니다.
이전 논의의 맥락에서 "페이지" 구성 요소는 Onboarding
템플릿을 사용하고 온보딩 프로세스에 대한 상태 관리를 처리합니다. 이 특정 페이지 구성 요소에 대한 코드는 여기에 제공되지 않지만 내 저장소에서 찾을 수 있습니다. 언급한 대로 페이지 구성 요소의 코드에는 특별한 것이 없습니다. 주로 상태를 관리하고 이를 Onboarding
템플릿에 전달하는 데 중점을 둡니다.
실제로 원자 디자인이 어떤 모습인지 살펴보겠습니다. 이 접근 방식의 장점과 단점을 살펴보겠습니다.
원자 설계는 모듈성, 재사용성, 유지 관리성과 같은 수많은 명백한 이점을 제공하지만 처음에는 고려해야 할 몇 가지 단점도 있습니다.
초기 설정 및 복잡성 : 원자 설계에는 잘 계획된 구조와 조직이 필요하며, 이는 초기 설정에 시간이 많이 걸리고 어려울 수 있습니다. 또한 특히 이러한 세부적인 접근 방식이 불필요할 수 있는 소규모 프로젝트의 경우 코드베이스가 더욱 복잡해질 수 있습니다.
학습 곡선 : 원자 설계를 처음 접하는 개발자의 경우 방법론은 가파른 학습 곡선을 가질 수 있습니다. 다양한 레벨과 이들이 어떻게 조화를 이루는지에 대한 확실한 이해가 필요하며, 이는 초보자에게 부담스러울 수 있습니다.
오버헤드 : 원자 설계를 구현하려면 다수의 작고 특수한 구성 요소를 만드는 것이 포함될 수 있습니다. 이로 인해 특히 구성 요소가 하나의 특정 컨텍스트에서만 사용되는 경우 이러한 구성 요소를 관리하고 유지 관리하는 데 오버헤드가 증가할 수 있습니다.
과도한 엔지니어링 위험 : 재사용 가능한 모듈식 구성 요소를 만드는 데 초점을 맞추면 개발자가 광범위한 응용 프로그램에 집중하는 대신 개별 구성 요소를 개선하는 데 너무 많은 시간을 소비할 수 있는 과도한 엔지니어링의 잠재적 위험이 있습니다.
의사소통 및 협업 : 원자 설계의 성공은 디자이너, 개발자 및 기타 이해관계자 간의 명확한 의사소통과 협업에 달려 있습니다. 공통 언어를 확립하지 못하거나 방법론을 이해하지 못하면 구현 시 혼란과 불일치가 발생할 수 있습니다.
그러나 이 접근 방식에는 이미 언급한 장점이 있습니다. 이에 대해 더 자세히 이야기합시다.
확장성 : 디자인을 가장 기본적인 요소로 분해함으로써 구성 요소의 복잡성을 구축하는 것이 더욱 관리하기 쉬운 작업이 되었습니다. 원자를 만드는 데는 몇 가지 어려움이 있었지만 이러한 원자를 기반으로 구성 요소를 만드는 것은 매우 즐거웠습니다.
효율성 : 원자, 분자 및 유기체를 재사용하는 능력은 새로운 기능을 설계하고 개발하는 데 소요되는 시간을 크게 줄여줍니다. 기본 구성 요소가 설정되면 새 인터페이스를 만드는 것은 기존 요소를 결합하는 것만큼 간단할 수 있습니다.
일관성 : 이전 지점에서 직접 나옵니다. 동일한 원자, 분자 및 유기체가 여러 템플릿과 페이지에서 사용되므로 사용자 인터페이스가 균일하게 유지되어 사용자에게 원활한 경험을 제공합니다.
문서화 : 원자 디자인은 본질적으로 문서화를 지원합니다. 원자 기반 구조는 구성 요소를 구축하고 사용하는 방법에 대한 명확하고 시각적인 가이드 역할을 할 수 있습니다. 이는 특히 새로운 팀 구성원을 온보딩하는 데 도움이 될 수 있습니다.
유지 관리성 : 원자 디자인의 가장 큰 장점 중 하나는 디자인 시스템의 유지 관리에 어떻게 기여하는지입니다. 모든 것을 원자 부분으로 분해함으로써 모든 변경이나 업데이트가 원자 수준에서 이루어진 다음 시스템을 통해 전파될 수 있습니다. 예를 들어 버튼의 색상을 변경하기로 결정한 경우 원자 수준에서 한 번만 변경하면 되며 이 버튼이 사용되는 모든 분자, 유기체 및 템플릿에 반영됩니다. 이는 시간이 지남에 따라 설계 시스템을 업데이트하고 유지 관리하는 프로세스를 크게 단순화합니다.
결론적으로, 원자 설계는 양날의 검처럼 보일 수 있지만(초기 설정 및 학습 곡선 측면에서 다소 어려운) 잠재적인 이점은 초기 어려움을 겪을 가치가 충분히 있습니다. 그리고 아무리 강력한 검이라도 숙련된 기사의 손에서는 무해하다는 점을 기억하십시오!