[미니 프로젝트] 25JS Ideas (4 of 27)- Pass the message

Seungrok Yoon (Lethe)·2023년 7월 20일
0
post-thumbnail

네 번째 미니 프로젝트 - Pass the message

이 프로젝트는 input태그에 사용자가 입력한 값을 다른 요소가 접근할 수 있도록 하는 것이 핵심이었습니다.

생각보다 금방 끝날 것 같으니, 구현을 다 마친 다음에는 리팩토링을 함께 진행해보도록 하죠!😆

PR: https://github.com/SeungrokYoon/25-vanilla-javascript-projects/pull/17
site: https://animated-pony-97da24.netlify.app/

완성된모습

기본 마크업

카드 디자인을 보고 크게 위에서 아래로 세 부분으로 나누면 좋겠다는 생각을 했습니다.

  • 제목이 있는 top
  • labelinput, 그리고 제출버튼이 있는 middle
  • input 의 값이 출력되는 bottom
    이렇게 말이죠.

이것 설계를 바탕으로 아래와같이 마크업구조와 스타일을 잡았습니다. 추가적인 스타일링은 기능구현 이후에 하도록 할게요.

 <body>
    <div id="root">
      <div class="container">
        <div class="card">
          <div class="card-top">
            <h3>Pass the message</h3>
          </div>
          <hr />
          <div class="card-middle">
            <label for="message-input">Enter a message</label>
            <div>
              <button>icon</button><input id="message-input" type="text" />
            </div>
            <button id="submit">Submit</button>
          </div>
          <div class="card-bottom">
            <p id="output-message"></p>
          </div>
        </div>
      </div>
    </div>
    <script type="module" src="main.js"></script>
  </body>

기능구현

기능구현의 핵심은 버튼의 클릭이벤트, keydown 이벤트를 활용해서 input의 값을 카드컴포넌트 하단의 p태그에 업데이트하는 작업이었습니다.

keydown event

https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event

기능구현이 완료된 모습은 다음과 같습니다.

버튼을 클릭할 때, 엔터키를 누를 때 동일한 로직이 반복되는 것 같아

submitMessage함수로 값을 주는 쪽과 받는 쪽을인자로 받을 수 있도록 개선했습니다.

기능구현코드

const inputEl = document.getElementById('message-input');
const submitBtn = document.getElementById('submit');
const outputMessageEl = document.getElementById('output-message');

function submitMessage({ start, destination }) {
  const nextText = start.value;
  start.value = '';
  const currentDestinationText = destination.innerHTML;
  const isNewTextNotEmpty = nextText.length;
  const shouldUpdate = isNewTextNotEmpty && currentDestinationText !== nextText;
  shouldUpdate ? (destination.innerHTML = nextText) : '';

  if (!isNewTextNotEmpty) alert('The message is empty!');
}

submitBtn.addEventListener('click', (e) => {
  submitMessage({ start: inputEl, destination: outputMessageEl });
});

inputEl.addEventListener('keydown', (e) => {
  if (e.code === 'Enter') {
    submitMessage({ start: e.target, destination: outputMessageEl });
  }
});

스타일 추가

Google Material Icon 사용해보기

이메일 아이콘을 인풋 태그 옆에 넣을 예정입니다.

Google Material Icon은 폰트더라구요. 그래서 만약 아이콘을 기본 형태가 아닌 다른 형태 (outlined, two tone, round, sharp) 로 사용하고 싶다면, 구글 폰트 API를 사용하는 방식으로 스타일에 맞는 폰트를 불러와야서 HTML에 추가해줘야합니다.

이렇게요!

<link
      href="https://fonts.googleapis.com/css?family=Material+Icons|Material+Icons+Outlined|Material+Icons+Two+Tone|Material+Icons+Round|Material+Icons+Sharp"
      rel="stylesheet"
    />

이렇게 하고 나면, 다운로드받은 폰트에 따라 특정 클래스이름(material-icon, material-icon-outlined, 등등)에 자동으로 스타일이 적용되게 됩니다.

아이콘을 사용할 때는 아래와 같이 클래스 이름 + 클래스 요소 내부에 아이콘 이름 을 명시해 사용해주시면 되겠어요!

<span class="material-icons-outlined">
home
</span>

Hexcolor code에 투명도를 주고 싶을땐?

내가 사용하는 색상코드에 rgba처럼 불투명도를 주고 싶었습니다.
그렇지만 hexcode를 매번 rga로 변형하고, 거기에 alpha코드를 부여하는 것은 너무 불편해요.

그래서 검색해보았습니다. 투명도 수치를 4bit의 hexadecimal(16진수)로 표현해서 추가할 수가 있었습니다.

https://gist.github.com/lopspower/03fb1cc0ac9f32ef38f4

React의 관점에서 리팩토링해보기

만약 이 프로젝트를 리액트로 제작했다면, 저는 카드의 레이아웃을 별도로 만들어, top, middle, bottom에 각각 children을 전달했을 것 같습니다.

//상상해본 리액트 컴포넌트 예시1
<MessageCardLayout 
	top={<h3>Pass the message</h3>} 
    middle={<>
         <label for="message-input">Enter a message</label>
            <div>
              <button>icon</button><input id="message-input" type="text" />
            </div>
         <button id="submit">Submit</button>} bottom={}/>
         </>
   bottom={<p id="output-message"></p>}
/>

만약 top에는 h3태그만 사용해야한다처럼 디자인이 확정되었다면, props로 텍스트만 전달해도 되겠네요.

또한 인풋태그와 아이콘은 하나의 컴포넌트로 추상화했을 것 같아요.

//상상해본 리액트 컴포넌트 예시2
<MessageCardLayout 
	top={"Pass the message"} 
    middle={<>
         <InputWithIcon src={} withIcon={true}/>
         <button id="submit">Submit</button>} bottom={}/>
         </>
   bottom={<p id="output-message"></p>}
/>

생각해보면 제출버튼은 인풋과 매우 밀접한 관련이 있는데요, 이것은 어떻게 해야할까요?

회고

역시 디테일한 스타일링은 집중력이 요구됩니다.

google mateiral icon 가운데정렬

아이콘이 버튼의 가운데정렬이 되지 않아서 고생했어요.

구글 아이콘은 <span>로 이루어져있어서 span태그에 vertical-align:middle을 적용해 세로가운데정렬을 시켜주었습니다.

input 요소 overflow:hidden주기

input요소에 overflow:hidden을 주지 않으면, input요소가 입력받은 값들이 다 표시가 되면서 컨테이너 밖으로 삐져나올 수 있습니다.

flex:1을 주어도 일정 부분 미만으로 줄어들지 않는 기이한 현상을 발견할 수도 있으니 앞으로 주의해야겠습니다.

  • overflow:hidden이 없을 때
    인풋이 삐져나옵니다ㅠㅠ.
  • overflow:hidden이 주어졌을 때
    제대로 줄어들고 있네요^^.

배포 후 IOS 기기에서 확인 결과 스타일이 의도한 것과 다른 것을 확인!

프로젝트를 배포 후에 Iphone 11 Pro기기에서 크롬에 접속하여 내 프로젝트를 확인해보았다. 그런데 웬걸? 버튼 텍스트 색이 퍼렁색이 되어있었다! 😅

개발하면서 개발자도구로 모바일 기기에 대한 스타일 확인을 하면서 작업을 했는데 이 경우는 발견하지 못했다.

검색을 해보니 나와 비슷한 일을 겪은 개발자들이 있었다.

https://developer.apple.com/forums/thread/690529

그래서 나도 모바일 브라우저의 개발자도구를 열기위해서 노트북에 아이폰을 연결하고 사파리에서 문제가 된 페이지에 대한 개발자도구로 확인했더니 ...

두둥! color: -apple-system-blue? 가 button 하위요소에 적용이 되어 있었습니다. 이 녀석이 IOS 기기의 기본 색상을 반영하는 녀석이었나봅니다.

공교롭게도 Material Icon에서는 color를 따로 지정하고 있지 않았습니다.

그러니 button 태그에 명시적으로 color값을 주면 해결이 되겠군요. color속성은 상속이 되니까요.

버그픽스 커밋

결과는~

다행히 검은색으로 아이콘과 버튼텍스트가 출력이됩니다 야호~^^

이런 이슈가 있었다니... 처음알게되었어요. 스타일링할 때 주의해야겠군요.

profile
안녕하세요 개발자 윤승록입니다. 내 성장을 가시적으로 기록하기 위해 블로그를 운영중입니다.

2개의 댓글

comment-user-thumbnail
2023년 7월 20일

아주 유익한 내용이네요!

1개의 답글