SLASH 기술 블로그

Atomic Design 예시 본문

프론트엔드

Atomic Design 예시

SLASH 2021. 5. 4. 13:12
반응형

목표

이전 글에서 Atomic Design 패턴에 대해서 알아보았다.

이번에는 Atomic Design 패턴을 통해 직접 간단한 페이지 하나를 만들 예정이다.

 

사용한 기술 정리

[x] React.js

[x] Styled-Components

 

시작하기

우선 시작하기에 앞서 이번 글에서는 어떤 페이지를 만들것인지 캡쳐 화면을 보고 가보자!

저번 글에서 예시로 보여줬던 화면과 비슷한 디자인으로 이미지와 같은 페이지를 만들어 볼 것이다.

조금 다른 것은 Pages단계를 보여주기 위해서 텍스트를 입력하고 SEARCH 버튼을 누르면 Content영역에 해당 텍스트가 보여지는 기능을 추가해볼 것이다. 완성된 페이지를 같이 만들어보자.

 

Atomic 하게 구조 나누기

Atomic Design Pattern에서 가장 중요하게 생각하는 것은 바로 Component를 어떻게 정의 할 것인가 이다.

왜냐하면 Atoms과 Molecules Components를 최대한 잘 만들어 놓는게 Atomic Design의 핵심이기 때문이다.

 

우선 자신의 프로젝트에서 src폴더 안에 다음과 같이 폴더 구조를 만들어주자.

Pages단계는 pages폴더에 별도로 모아놓을 것이다. (pages폴더는 "_"를 붙이지 않았기 때문에 이미지에서 생략한다.)

폴더를 보면 모든 폴더 앞에 "_"를 붙인 것을 볼 수 있는데, 이건 꼭 붙이지 않아도 된다.

내가 Atomic Design Pattern으로 사이트를 만들었을 때 다른 사람들이 만든 폴더가 알파벳 순서상 Atomic 구조 폴더 사이에 들어가게 되면 나중에 폴더 구조를 볼때마다 불편함을 느껴서 프로젝트 자체가 Atomic Design Pattern으로 이루어져 있다면 이런식으로 모아 놓는게 가독성 면에서 좋기 때문에 이렇게 했다.

 

Atoms

먼저 우리가 이번에 만들 Atom 컴포넌트들을 먼저 확인 해보자.

우리가 만들 Atom 컴포넌트는 총 3개이다.

Button과 Input은 SearchBar를 만들때 사용할 것이고, Text는 페이지에 쓰는 모든 Text를 처리할 때 사용 할 것이다.

 

Button.jsx

import React from 'react'
import styled from 'styled-components'

const ButtonBox = styled.button`
  width: 120px;
  height: 40px;
  color: #f5f4dc;
  font-size: 1.8rem;
  font-weight: bold;
  background-color: #c26105;
  border: 0;
  cursor: pointer;
`

const Button = ({ text, onClick }) => {
  return <ButtonBox onClick={onClick}>{text}</ButtonBox>
}

export default Button

코드를 보게 되면 button에 대한 최소한의 정의만 되어 있다.

버튼에 들어갈 text와 onClick이벤트만을 받아서 처리한다.

 

Input.jsx

import React from 'react'
import styled from 'styled-components'

const InputBox = styled.input`
  width: 180px;
  height: 40px;
  color: #4b3f3c;
  font-size: 1.8rem;
  font-weight: bold;
  line-height: 40px;
  background-color: #120000;
  outline-style: none;
  border: 0;
  padding: 0 1rem;
`

const Input = ({ className, onChange }) => {
  return <InputBox 
    className={className} 
    placeholder={'ENTER KEYWORD'}
    onChange={(e) => onChange(e.target.value)}
  />
}

export default Input

Input 컴포넌트도 onChange이벤트 만을 받아서 처리하는 기능 밖에 없다.

 

Text.jsx

import React, { Fragment } from 'react'
import styled from 'styled-components'

const DefaultText = styled.span`
  font-size: 1.2rem;
`
const SmallText = styled.span`
  font-size: 0.5rem;
`
const LargeText = styled.span`
  font-size: 1.8rem;
`

const Text = ({ className, type = 'default', value }) => {
  return (
    <Fragment>
      { type === 'default' && <DefaultText className={className}>{value}</DefaultText> }
      { type === 'small' && <SmallText className={className}>{value}</SmallText> }
      { type === 'large' && <LargeText className={className}>{value}</LargeText> }
    </Fragment>
  )
}

export default Text

우리가 만들 페이지에서 사용하는 모든 텍스트는 이 컴포넌트로 처리할 것이다. type을 받아서 type에 따른 텍스트 크기를 보여주는 컴포넌트이다.

 

보면 Atom으로 정의한 컴포넌트들은 html 태그도 하나만 쓰이고 간단한 조건문 밖에 없는 단순한 컴포넌트라는 것을 확인 할 수 있다.

그러면 이 최소한으로 정의한 컴포넌트로 SearchBar를 만들어 보자.

 

Molecules

이번 페이지에 필요한 Molecule 컴포넌트는 검색할 때 필요한 SearchBar 컴포넌트 밖에 없다.

Text와 Input Atom을 조합하여 입력 양식 Molecule 컴포넌트도 만들어 볼 수 있겠지만 이번 페이지에서는 필요 없으니 SearchBar 컴포넌트만 만들어 보자.

 

SearchBar.jsx

import React from 'react'
import styled from 'styled-components'
import Text from '@/_Atoms/Text'
import Input from '@/_Atoms/Input'
import Button from '@/_Atoms/Button'

const Container = styled.div`
  display: inline-block;
`
const AreaBox = styled.div`
  display: flex;
  ${({ type }) => type === 'title' && `
    height: 20px;
    line-height: 20px;
  `}
`
const SearchBarInput = styled(Input)`
  margin-right: 10px;
`

const SearchBar = ({ onChange, onClick }) => {
  return (
    <Container>
      <AreaBox type={'title'}>
        <Text value={'SEARCH THE SITE'}/>
      </AreaBox>
      <AreaBox>
        <SearchBarInput onChange={onChange}/>
        <Button text={'SEARCH'} onClick={onClick}/>
      </AreaBox>
    </Container>
  )
}

export default SearchBar

코드를 보면 _Atmos폴더에 있는 Text, Input, Button을 가져와서 합쳐서 만든 것을 볼 수 있다. 이렇게 Molecule단계에서는 Atom 컴포넌트들을 조합하여 만들 수 있는 최소한의 동작 요소를 만들어야 한다.

이렇게 만든 SearchBar는 다음과 같은 모습을 한다.

Text 컴포넌트를 통해 "SEARCH THE SITE"라는 Title과 Input 컴포넌트Button 컴포넌트를 통해 검색을 할 수 있는 영역을 만들었다.

이렇게 세 가지의 Atom 컴포넌트를 조합하면 SearchBar라는 Molecule 컴포넌트를 만들 수 있다.

 

Organisms

이 단계에서는 Atom컴포넌트인 Text와 Molecule컴포넌트인 SearchBar를 조합하여 상단에 쓰이는 헤더를 만들 것이다.

import React from 'react'
import styled from 'styled-components'
import SearchBar from '@/_Molecules/SearchBar'
import Text from '@/_Atoms/Text'

const HeaderContainer = styled.header`
  border-bottom: 2px solid #010101;
`
const Navigation = styled.nav`
  height: 80px;
  display: flex;
  justify-content: space-between;
`
const Content = styled.div`
  width: 100%;
  padding: 0 4vw;
  display: flex;
  justify-content: space-between;
`
const NavigationSection = styled.nav`
  justify-content: flex-start;
  display: flex;
  align-items: center;
  flex: 1;
`
const ItemSection = styled.ul`
  justify-content: flex-start;
  display: flex;
  flex: 1;
`
const Item = styled.li`
  position: relative;
  width: auto;
  list-style: none;
  padding: 1rem;
`
const SearchSection = styled.a`
  display: inline-block;
  white-space: nowrap;
  flex-basis: auto;
  width: auto;
`
const MenuText = styled(Text)`
  font-weight: bold;
`

const Header = ({ onChange, onClick }) => {
  return (
    <HeaderContainer>
      <Navigation>
        <Content>
          <NavigationSection>
            <ItemSection>
              <Item><MenuText type={'large'} value={'Home'}/></Item>
              <Item><MenuText type={'large'} value={'About'}/></Item>
              <Item><MenuText type={'large'} value={'Blog'}/></Item>
              <Item><MenuText type={'large'} value={'Contact'}/></Item>
            </ItemSection>
            <SearchSection>
              <SearchBar onChange={onChange} onClick={onClick}/>
            </SearchSection>
          </NavigationSection>
        </Content>
      </Navigation>
    </HeaderContainer>
  )
}

export default Header

코드를 보면 nav태그와 ul, li태그를 통해서 영역만 잡아주고 실제로 보여주는 텍스트나 검색창은 Atom과 Molecule컴포넌트를 사용한 것을 알 수 있다. 이렇게 만들어진 Header 컴포넌트는 다음과 같은 모양이 된다.

그럴듯한 페이지가 완성되가고 있다.

조금만 더 힘내서 Template 컴포넌트와 Page 컴포넌트를 만들면 끝이다.

 

Templates

Templates 단계에서는 두개의 컴포넌트를 만들건데, 나는 Page전체에 대한 레이아웃의 해당하는 PageTemplate과 만들지는 않을거지만 About, Blog, Contact등 다른 페이지 마다 다른 컨텐츠가 들어가는 컨텐츠부분의 레이아웃을 나눠서 생각했다.

우리는 메인페이지 하나만을 만들 것이니까 MainContent 컴포넌트와 PageTemplate컴포넌트를 만들어보자.

 

MainContent.jsx

import React from 'react'
import styled from 'styled-components'
import Text from '@/_Atoms/Text'

const Container = styled.div`
  height: 120px;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  border: 2px solid #101010;
  margin: 20px 4vw;
`

const MainContent = ({ contentText }) => {
  return (
    <Container>
      <Text type={'large'} value={'Search Button Click! Show Keyword'} />
      <Text type={'large'} value={`CurrentKeyword: ${contentText}`} />
    </Container>
  )
}

export default MainContent

막상 보면 별게 없다. 왜냐면 우리는 입력한 검색어를 화면에 보여주기만 하면 되기 때문이다. (실제 사이트들은 다양한 컨텐츠가 들어가기 때문에 이부분이 꽤 복잡해진다.)

 

PageTemplate.jsx

import React from 'react'
import styled from 'styled-components'
import Header from '@/_Organisms/Header'
import MainContent from '@/_Templates/Content/MainContent'

const Template = styled.div``

const PageTemplate = ({ type, onChange, onClick, contentText }) => {

  return (
    <Template>
      <Header onChange={onChange} onClick={onClick}/>
      { type === 'main' && <MainContent contentText={contentText}/> }
    </Template>
  )
}

export default PageTemplate

여기는 더 간단하다. 일단 모든 페이지에 들어가는 Header는 공통으로 쓰이고 페이지 Type에 따라 다른 컨텐츠 템플릿을 보여주는게 전부이다.

이렇게 만들어진 페이지는 다음과 같은 모양이 된다.

Templates단계에서는 마치 HTML와이어 프레임과 같은 목업 형태까지라고 설명하고 있는데, 실제로 기능은 없는 HTML 와이어 프레임 단계 까지 완성하였다. 이제 이렇게 만든 템플릿을 이용해서 기능이 있는 페이지를 만들어보자.

 

Pages

우리는 메인 페이지 하나만 존재하기 때문에 이렇게 만들었다.

바로 코드를 확인 해보자.

import React from 'react'
import PageTemplate from '@/_Templates/PageTemplate'

const Main = () => {
  const [contentText, setContentText] = React.useState('')
  const [keyword, setKeyword] = React.useState('')

  const onChange = (value) => {
    setKeyword(value)
  }
  const onClick = () => {
    setContentText(keyword)
  }
  
  return <PageTemplate 
    type={'main'} 
    onChange={onChange} 
    onClick={onClick} 
    contentText={contentText}
  />
}

export default Main

코드를 보면 onChangeonClick에 대한 정의가 되어 있고 PageTemplate에 필요한 함수와 정보를 내려주고 있다.

이렇게 우리가 최종적으로 만들려 했던 검색창에 값을 입력하고 SEARCH버튼을 누르면 컨텐츠 영역에 보여지는 페이지를 만들어 보았다.

 

마무리

이번 글을 통해 다같이 Atomic Design Pattern을 통해 간단한 페이지 하나를 만들어 볼 수 있다.

여기서 핵심은 우리가 만든 페이지에서 사용한 모든 글자는 Atom단계에서 정의한 Text컴포넌트에 의해 만들어 졌다는 것이다.

이렇게 최소단위의 컴포넌트를 정의하고 조합하여 하나의 페이지를 만드는 방법이 Atomic Design이다.

 

이 글을 통해 많은 사람들이 Atmic Design에 대해 이론적인 것 뿐만 아니라 직접 구현까지 가능한 사람이 되었으면 좋겠다.

 

@JJoGeon

반응형
Comments