728x90

*하루동안 새롭게 알게된 부분, 에러를 해결한 방법 등을 작성하는 개발일기입니다. 다른 사람에게도 설명해줄 수 있도록 제 머릿속에 넣기 위해 정리를 시작하게 되었습니다. 

java.sql.SQLNonTransientConnectionException : Public Key Retrieval is not allowed 에러 해결

MySql이 8.x 이상인 경우 발생하는 에러로, jdbc urlallowPublicKeyRetrieval=true 설정을 추가해주어야 한다고 한다. 

 

그런데 나의 경우는, 저 설정 뿐만 아니라(기존에 설정되어 있던 'useSSL=false' 외에) 'useUnicode=true'와 'characterEncoding=UTF-8'도 추가로 설정해주니 에러가 해결되었다. 

 

spring.datasource.url=jdbc:mysql://localhost:3306/db-name?useUnicode=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&useSSL=false

 

Auth(HOC)

회원가입과 로그인 기능을 제공하는 리액트 사이트를 만들고 있다. 그렇다보니 누구나 진입이 가능한 페이지도 있고, 로그인한 회원만 진입이 가능한 페이지, 로그인한 회원은 진입하지 못하는 페이지가 있다. 이때, 인증을 위해 HOC, High Order Component function을 사용한다. 

 

High Order Component function는 컴포넌트를 인자로 받아서 새로운 컴포넌트를 리턴하는 함수이다. 서버로부터 응답 받은 유저의 상태(로그인/로그아웃/관리자)에 따라 Auth function에 인자의 값을 다르게 줌으로써 페이지의 인증을 컨트롤할 수 있다.

 

src/hoc/auth.js에 다음과 같은 파일을 만든다. 

 

import { useEffect } from 'react';
import { useSelector } from 'react-redux';

export default (Component, option, adminRoute = null) => {
    /*
    option: null => 누구나 출입가능한 페이지
          : true => 로그인한 유저만 출입가능한 페이지 
          : false => 로그인한 유저는 출입 불가능한 페이지 
    */

    const AuthenticateCheck = (props) => {
        const isLoggedIn = useSelector((state) => state.user.isLoggedIn);

        useEffect(() => {
            // 로그인 되어 있지 않다면, logOutMain으로 이동시키기
            if (!isLoggedIn && option) {
                props.history.push('/');
            }
        }, []);

        return <Component />;
    };

    return AuthenticateCheck;
};

 

 

이제 인증이 필요한 페이지에 다음과 같이 적용해준다. 두 번째 인자가 option이고, option이 null이면 누구나 출입 가능한 페이지, true로그인한 사용자만 출입 가능한 페이지, false로그인한 사용자는 출입할 수 없는 페이지가 된다. 

 

import { useState } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

import Auth from './hoc/auth';
import LogOutMain from './components/LogOutMain';
import Main from './components/Main';
import LogInPage from './components/LogInPage';
import SignUpPage from './components/SignUpPage';

const App = () => {
    return (
        <Router>
            <Switch>
                <Route exact path="/" component={Auth(LogOutMain, false)} />
                <Route exact path="/login" component={Auth(LogInPage, false)} />
                <Route exact path="/signup" component={Auth(SignUpPage, false)} />
                <Route exact path="/main" component={Auth(Main, true)} />
                <Route path="/*">404 Not Found</Route>
            </Switch>
        </Router>
    );
};

export default App;
728x90
728x90

해외 디자인 아티클 번역 뉴스레터 '디독'의 'OK 버튼은 왜 오른쪽에 있을까?'를 정리한 글입니다. 

 

디자이너들은 종종 대화 상자에서 'OK'와 '취소' 버튼이 어떤 배치로 있는 것이 좋은지 고민한다고 한다. 

 

'OK' 버튼은 태스크를 완료하기 위한 핵심 동작이고, '취소' 버튼은 사용자가 작업을 완료하지 않고 원래 스크린으로 돌아가기 위한 보조 동작이다. 

 

'취소' 버튼은 'OK' 버튼 앞에 와야 할까? 뒤에 와야 할까? 

 

일부 디자이너들은 보조 액션보다 주된 액션을 왼쪽에 두는 것더 가깝고, 클릭하기까지의 시간이 적기 때문에 사용자에게 더 좋을 것이라고 생각한다고 한다. 

 

하지만, 사용자가 어떤 행동을 하기 전에 모든 옵션을 본다는 사실을 무시할 수는 없다. 즉, 대부분의 사용자가 옆에 있는 보조 액션 버튼을 보지 않고 주된 액션 버튼을 맹목적으로 클릭하지는 않는다는 뜻이다. 

 

만약, 'OK' 버튼이 왼쪽에 있고 '취소' 버튼이 오른쪽에 있다면, 'OK' 버튼을 보고, '취소' 버튼을 보고, 다시 'OK' 버튼을 볼 것이다. 

 

즉, 'OK' 버튼이 왼쪽에 있을 때 시각적인 고정이 더 많아지고, 여러가지 방향으로 흐르게 된다

 

 

아래처럼 'OK' 버튼이 오른쪽에 있다면 시각적인 고정이 줄어들고, 한 방향으로 흐를 수 있다

 

즉, 주된 액션을 왼쪽에 두면 더 쉽게 도달할 수는 있지만, 정신적인 프로세스와 시각적 고정의 관점에서 주된 액션을 오른쪽에 두는 게 실제로 더 빠르다

728x90

'Front-end > UIUX' 카테고리의 다른 글

UX에서 White Space가 중요한 이유  (0) 2020.11.08
728x90

*하루동안 새롭게 알게된 부분, 에러를 해결한 방법 등을 작성하는 개발일기입니다. 다른 사람에게도 설명해줄 수 있도록 제 머릿속에 넣기 위해 정리를 시작하게 되었습니다. 

JavaScript 배열에서 max 값 찾기(spread operator)

JavaScript 배열에서 max 값을 찾기 위해서는 spread operator(전개 연산자)를 사용해야 한다.

 

배열 num의 max 값을 찾기 위해 아래 코드처럼 그냥 바로 Math.max(num) 하면 안된다는 것이다. 

 

const num = [1, 3, 5, 7, 9]; 

console.log(Math.max(num)); 
// 결과: Nan

 

Math.max()입력된 숫자 중 가장 큰 수를 반환한다. 배열에서 가장 큰 값을 찾기 위해 배열을 Math.max의 매개변수로 바로 넣어버리면 배열을 숫자로 인식하지 않기 때문에 Nan을 반환하는 것이다. 

 

즉, Math.max([1, 3, 5, 7, 9])의 형태가 아닌 Math.max(1, 3, 5, 7, 9) 형태로 들어가야 Max를 제대로 반환하게 된다. 

 

[1, 3, 5, 7, 9]1, 3, 5, 7, 9로 바꾸어 줄 수 있는 것이 spread operator(...), 전개 연산자이다. 

 

MDN에서는 spread operator를 다음과 같이 설명하고 있다. 

 

> 전개 구문을 사용하면 배열이나 문자열과 같이 반복 가능한 문자를 0개 이상의 인수 (함수로 호출할 경우) 또는 요소 (배열 리터럴의 경우)로 확장하여, 0개 이상의 키-값의 쌍으로 객체로 확장시킬 수 있습니다.

 

즉, 배열을 요소로 바꾸어주는 것이라고 생각하면 된다. 

 

그래서 배열에서 max 값을 찾고자 한다면, 아래처럼 배열 이름 앞에 spread operator를 넣어주면 된다. 

 

const num = [1, 3, 5, 7, 9]; 

console.log(Math.max(...num)); 
// 결과: 9

 

배열이 특정 요소를 포함하고 있는지 판별(Array.includes())

특정 요소가 몇 번째 인덱스에 있는지 확인하고 싶을 때는 Array.indexOf(),

특정 요소를 배열이 포함하고 있는지 확인하고 싶을 때는 Array.includes() 

 

for ... in, for ... of 

for ... in은 객체 key 순환, 

for ... of는 객체 value 순환

 

서버에서 로그인 인증하는 법 

프론트에서 id와 password를 보내면, 백에서는 받아온 id를 먼저 찾고, 이 id가 가지고 있는 password와 받아온 password가 같은지 확인한다. 

 

dispatch가 먼저일까, 서버와의 통신이 먼저일까

action을 dispatch하는 게 먼저일지 서버로 데이터를 보내는 게 먼저일지를 고민했다. 

 

그런데 아래처럼, axios post한 결과가 succeed냐, failed냐에 따라 dispatch 해야하는 action이 달라진다면, 서버와 먼저 통신하고, action을 dispatch하는 게 맞을 것이다. 

 

export function loginRequest(username, password) {
  return (dispatch) => {
      dispatch(login());
 
      // API REQUEST
      return axios.post('/api/account/signin', { username, password })
      .then((response) => {
          // SUCCEED
          dispatch(loginSuccess(username));
      }).catch((error) => {
          // FAILED
          dispatch(loginFailure());
      });
  };
}
728x90
728x90

*하루동안 새롭게 알게된 부분, 에러를 해결한 방법 등을 작성하는 개발일기입니다. 다른 사람에게도 설명해줄 수 있도록 제 머릿속에 넣기 위해 정리를 시작하게 되었습니다. 

react redux axios post 

회원가입 폼 제출 시, 해당 user가 넘긴 정보(nickname, phone, role)가 dispatch되고 axios post를 하는 코드를 작성했다.

 

여기서 잠깐 redux 개념을 정리하고 가자면,,, 

redux데이터의 중앙 저장소이다. 각 컴포넌트에서 필요로 할때 꺼내 쓸 수 있는 장소라고 생각하면 된다. 

 

redux에서 data를 바꾸기 위해서action이 필요하고, action을 dispatch하면 중앙 저장소의 data가 변경된다. 그런데 action을 dispatch한다고 data가 자동으로 바뀌는 것은 아니고, reducer에서 어떻게 바꿀지를 구현해주어야 한다.

 

회원가입 폼을 제출하면, registerUser 액션을 dispatch 중앙저장소에 user data를 추가해준다.

 

// components > SignUpPage.js

const onSubmitForm = useCallback(
        async () => {
            const nickname = await makeNickname();
            await dispatch(registerUser({ role, phone, nickname }));
        },
        [role, phone],
);

 

registerUser 액션은 registerAction을 dispatch하고, axios post를 통해 해당 서버로 data를 넘겨준다.

 

// reducers > user.js

export const registerUser = async (data) => {
    return (dispatch) => {
        dispatch(registerAction(data));
        return axios.post('http://localhost:8080/users', data);
    };
};

export const registerAction = (data) => {
    return {
        type: REGISTER_USER,
        data,
    };
};

 

JavaScript insertion sort(삽입 정렬)

삽입 정렬은 array의 두번째 인덱스에서 시작해서 마지막 인덱스가 될때까지, 앞의 요소들과 비교해 작으면 삽입하는 정렬 방식이다. 

 

function solution(arr){
  const answer = arr;

  for(let i = 1; i < arr.length;  i++){
  	
    // tmp는 비교대상이 될 임시적 변수 
    const tmp = arr[i]; 
    
    // j는 tmp의 앞 인덱스
    let j = i - 1
    
    for(; j >= 0; j--){
      if(tmp < arr[j]){
        arr[j+1] = arr[j]; 
      } 
      
      else{
        // tmp가 더 크면 더 이상 비교할 필요 없다. 앞의 요소들은 이미 정렬된 상태이므로
        break; 
      }
    }
    
    // j가 -1이 된 경우, 즉 모든 요소가 tmp보다 컸다면 arr[0]에 tmp에 들어가야 하고
    /*
    	중간에 break된 경우, 
        예를 들면 j가 2일때 break 되었다면, 
        arr[2]보다는 tmp가 크고, arr[4]보다는 tmp가 작다는 뜻이므로
        j가 3인 자리에 tmp가 들어가야 한다. 
    */
    // 즉, 둘 중 어떤 경우라도 항상 인덱스가 (j + 1)인 자리에 tmp가 들어가야 한다.
    arr[j+1] = tmp; 
  }

  return answer; 
}

let arr = [11, 7, 5, 6, 10, 9]; 
console.log(solution(arr));

 

JavaScript Array.slice()

slice()는 배열에서 특정 인덱스부터 특정 인덱스까지의 복사가 필요할 때 주로 쓰인다. 즉, 원본 배열은 건드리지 않는 메소드이다. 

 

arr.slice(begin, end) 형태로 쓰이며, 'begin'번째 인덱스부터 'end - 1'번째 인덱스까지 복사된다. 즉, end 인덱스는 미포함이다.    

 

const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];

console.log(animals.slice(2, 4));
// 결과: ["camel", "duck"]
// 2번째 인덱스부터 (4-1)번째 인덱스까지 복사, 즉, 4번째 인덱스는 미포함

// 배열 전체를 복사하고 싶을 때
console.log(animals.slice());

console.log(animals.slice(2));
// 결과: ["camel", "duck", "elephant"]
// 2번째 인덱스부터 끝 인덱스까지 복사됨

 

 

728x90
728x90

*하루동안 새롭게 알게된 부분, 에러를 해결한 방법 등을 작성하는 개발일기입니다. 다른 사람에게도 설명해줄 수 있도록 제 머릿속에 넣기 위해 정리를 시작하게 되었습니다. 

 

React에서 Form을 사용해서 구현할 때 다양한 input을 받아야 하는 경우, 관리할 state가 많아지면서 코드가 쉽게 복잡해질 수 있다. 

 

또, React에서는 Form 양식을 제출할 때, 성가신 것들이 몇 가지 있다. 

 

  • Form 상태에서 값 가져오기
  • 유효성 검사 및 오류 메세지
  • Form submit 핸들링 

Formik라는 라이브러리를 사용하면 위의 세 가지 요소를 한 곳에 배치할 수 있어 편리하다고 한다. Formik는 Provider를 통해 상태와 메소드를 ui에 공유한다. 이에 따라 form 내에 있는 모든 컴포넌트들이 동일한 상태를 공유할 수 있게 된다.  

 

위와 같은 장점들이 있어, 이번 프로젝트에서 Formik를 사용해볼까 했었는데, 내가 하는 프로젝트는 '시각 장애인들을 위한 커뮤니티'이다. 회원가입 시 번거로움을 덜어 드리고 싶어서, 전화번호만 입력을 받을 예정이라 그리 많은 input이 필요하지는 않아 그냥 React의 Form을 사용하기로 했다. 

 

오늘은 만약 사용자가 중복된 전화번호를 입력했을 경우, 폼이 submit되지 않고 경고창이 뜨고, 전화번호 input이 reset되는 것을 구현했다.

 

 

이 때, 만약 중복된 전화번호를 입력했다면 '회원가입하기'를 눌렀을 때 아래와 같이 경고창이 뜨고

 

 

전화번호 input창이 reset되게 말이다. 그대로 둘 수도 있겠지만, 그러면 시각장애인들이 다시 input을 지워야 하니 번거로울 것 같아서 그냥 아예 reset을 했다. 

 

 

중복확인을 하더라도 submit되어 data가 넘어가면 어쩌지 했는데, 만약 registered된 사용자라면, 'alert'문을 return하면 경고창만 띄워지고, data가 submit되지는 않았다. 

 

const onSubmitForm = useCallback(
        async (e) => {

            // 전화번호 중복 확인
            // if(){ //중복이라면
            //     setRegistered(true);
            // }

            // 중복이라면 경고 메세지 띄우고,
            if (registered) {
                setPhone('');
                return alert('이미 등록된 사용자입니다. 전화번호를 다시 입력해주십시오.');
            }
            const nickname = await makeNickname();
            await dispatch(registerAction({ role, phone, nickname }));
        },
        [role, phone],
);
728x90
728x90

*하루동안 새롭게 알게된 부분, 에러를 해결한 방법 등을 작성하는 개발일기입니다. 다른 사람에게도 설명해줄 수 있도록 제 머릿속에 넣기 위해 정리를 시작하게 되었습니다. 

 

JavaScript 배열에서 랜덤 값 추출하려면, 배열의 인덱스 값을 랜덤으로 뽑아주면 된다. 

 

예를 들어, 길이가 10인 배열에서 랜덤 값을 뽑아주려면, 0~9 중에서 하나의 인덱스가 뽑히면 된다. 

 

Math.random()은 0부터 1미만의 수를 랜덤하게 리턴하므로 범위가 0~9가 되려면 Math.random() * 10, 즉, Math.random() * array.length 한 결과의 소수점을 버림해주면 된다. 

 

const num = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 
const randomVal = num[Math.floor(Math.random() * num.length))]; 

 

인덱스는 정수이다. 그래서 (Math.random() * 10의 결과로 9.xx와 같이 소수점이 붙은 형태로 리턴될 수 있기 때문에) 소수점을 버려줘야 한다. 

 

0 <= Math.random() < 1
0 <= Math.random() * 10 < 10
728x90
728x90

*하루동안 새롭게 알게된 부분, 에러를 해결한 방법 등을 작성하는 개발일기입니다. 다른 사람에게도 설명해줄 수 있도록 제 머릿속에 넣기 위해 정리를 시작하게 되었습니다. 

 

스프링부트 프로젝트를 실행하려는데 'java.lang.unsupportedclassversionerror' 에러가 발생했다. 

 

프로젝트를 11버전으로 실행해야 하는데, 8버전으로 실행하려 하다보니 발생한 에러이다. 

 

나는 8버전과 11버전이 모두 설치되어 있었는데 환경변수에서 JAVA_HOME의 경로가 8버전으로 연결되어 있었다. 

 

이 경우에는 JAVA_HOME의 경로만 11버전으로 연결해주면 된다. 

 

우선, 제어판 > 시스템 환경 변수 편집에 들어가서 '환경변수'를 클릭한다. 

 

 

그리고 시스템 변수의 JAVA_HOME을 더블 클릭한다. 

 

 

변수 값에 버전 11이 설치되어 있는 bin 디렉토리 경로를 넣어준다. 

 

728x90
728x90

*하루동안 새롭게 알게된 부분, 에러를 해결한 방법 등을 작성하는 개발일기입니다. 다른 사람에게도 설명해줄 수 있도록 제 머릿속에 넣기 위해 정리를 시작하게 되었습니다.   

 

React + Spring Boot로 프로젝트를 하고 있는데, CORS 에러가 발생했다. 

 

CORS를 이해하기 위해서는 SOP를 알아야 한다. 

 

SOP(Same Origin Policy), 말그대로 같은 Origin에만 요청을 보낼 수 있게 제한하는 보안정책이다. Origin은 다음 세 가지로 구성되어 있다. 

  • URI Schema ex) http, https
  • Hostname ex) localhost, naver.com
  • Port ex)8080

세 가지 중 하나라도 구성이 다르면, SOP 정책에 걸리기 때문에 ajax 요청을 보낼 수 없다. 

 

그래서 CORS가 필요해졌다. 즉, 다른 Origin이라도 서로 요청을 주고 받을 수 있게 말이다. 

728x90
728x90

 

Redux란? 

Redux는 JavaScript app을 위한 predictable state container이다. React의 보완재 역할을 하는데, action에 반응하여 상태를 변경하기 때문에 React처럼 UI를 상태에 대한 함수로 기술하는 framework와 잘 어울린다. 

 

React에 Redux가 필요한 이유? 

React로 프로젝트를 진행했을 때, component는 local state를 가지고 application은 global state를 가지게 된다. local state각각의 component가 가지는 state, application은 이 state를 기반으로 만들어진다.  global state는 예를 들어, 유저의 로그인 유무에 따라 application의 state가 달리 보인다고 할 때, application 전체에서 global state는 유지, local state는 global state를 공유하게 되는 것이다. 

local state의 전달이 어려워서 

React로만 프로젝트를 진행하게 될 경우, application이 local state, global state를 관리하기 어렵다. 예를 들어, shopping mall에서 장바구니를 관리하기 위해 최상단 Component인 App.js에서 cart state를 만들고, 각각의 Component에 이를 props로 전달하여 프로젝트를 관리했다고 하자.

 

프로젝트 규모가 작을 때는 단 한 번의 data 이동으로 cart state가 전달된다. 하지만, 프로젝트의 규모가 커지고 Component 수가 늘어난다면 어떨까? 단순히 cart state를 CartItem.js로 전달하고 싶을 뿐인데, 이를 위해 cart props를 사용하지 않는 Component에도 cart props를 전달하게 된다

 

즉, 프로젝트 규모가 커질수록 props로 data를 전달하기 위해 위와 같은 필요 없는 data의 흐름이 생기게 된다. 그리고 만약 CartItem.js로 cart props가 제대로 전달이 되지 않을 경우, 중간에 낀 모든 Component에서 일일이 문제점을 찾아봐야 한다

 

global state의 유지가 어려워서 

Shopping mall을 포함한 대부분의 application에서는 로그인 기능이 구현되어야 한다. 유저의 개인 정보, 결제 정보 등이 있어야 하기 때문이다. 하지만 React만으로 프로젝트를 진행할 경우, global state를 모든 Component에 유지하기가 어렵다. 유저의 인증 정보를 모든 Component에 props로 계속해서 전달해야 하는데, 이때 복잡한 절차를 거쳐야 한다.

 

하지만 Redux를 사용하게 되면 하나의 store를 통해 global state를 포함한 모든 state를 저장하고 유지할 수 있게 되고, 원하는 Component로만 data를 전달할 수 있게 되어 위와 같은 문제가 해결된다.  

 

 

Flux 구조

Flux는 대규모 프로젝트에서 너무 복잡해지는 MVC 구조 단점을 보완하는 단방향 데이터 흐름의 구조이다. Redux는 Flux의 몇 가지 중요한 특성에 영감을 받아 개발되었다. Redux는 Flux 패턴을 좀 더 쉽고 정돈된 형태로 쓸 수 있게 도와주는 라이브러리라고 볼 수 있다.

 

Redux 기본 개념

Actions

Application의 store, 즉 저장소로 data를 보내는 방법. view에서 정의되어있는 action을 호출하면 action creators는 application의 state를 변경해준다.

 

Reducer

Action을 통해 어떠한 행동을 정의했다면, 그 결과 application의 상태가 어떻게 바뀌는지 특정하게 되는 함수. Action의 type에 따라 변화된 state를 반환하게 된다.

 

Store

'무엇이 일어날지'를 나타내는 action, action에 따라 상태를 수정하는 reducer를 저장하는 application에 있는 단 하나의 객체.

 

 

 

 

*잘 정리된 다른 분의 블로그 글을 참고, 요약했습니다. 

medium.com/@jsh901220/react%EC%97%90-redux-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-a8e6efd745c9

 

React에 Redux 적용하기(1)

리덕스(Redux)란?

medium.com

 

 

 

728x90

'Front-end > React' 카테고리의 다른 글

[React] 클립보드에 텍스트 복사하기  (0) 2021.05.05
next에 antd 적용 안 될 때  (0) 2021.04.10
React: props와 state  (0) 2020.10.25
728x90

UX에서 white space란?

White Space는 디자인 레이아웃을 만들 때 매우 유용한 테크닉이다. 

 

It's important when you're putting together a design layout that you let elements on the page breathe. The best way to do that is by introducing what's known as white space.

How to make White Space?

White Space를 만드는 것은 어렵지 않다. 각 element가 text든 image든 graphic이든 그들 사이에 공간을 만들어주면 된다. 각 요소들이 자신만의 visual focus를 받을 수 있도록 각 요소 사이에는 충분한 여백을 넣어줘야 한다. 

 

That way when someone is viewing your design they can get an easy feel for it and they can take on board what you are trying to say. Due to this, empty space is a legitimate design element that has a great influence on the user experience.

Negative Space, Empty Space라고도 불리는 White Space가 꼭 White일 필요는 없다. 어떤 컬러든, 어떤 텍스쳐든 패턴이든 Background Image든 상관 없다.

Types of White Space

Marcro space - Micro space

웹사이트와 모바일 어플을 위한 UI Design을 할 때, White Space는 high usability와 navigation ability of the interface에 매우 중요한 역할을 한다.

 

두 가지 타입의 공간이 있는데, 
1. Macro space: This term refers to spaces between the main elements of a web page or mobile and the space around each part. 
2. Micro space: These are small gaps within an element. Line spacing in the text, gaps between pictures, separators, and more.

Why is negative space important? 

디자이너/클라이언트들은 되도록 많은 요소, 기능들을 한 페이지에 추가해야 소비자들에게 유용할 것이라고 생각하지만, 아니다.

 

Users do not need everything at once.

충분한 여백 없는 너무도 많은 요소들(Overloaded with information and interactive elements)은 distraction의 level을 높일 뿐이다. 사용자들은 그속에서 자신이 필요한 것을 찾기 위해 시간을 더 들이게 된다.

 

White space leaves separation between the content, it makes the important things stand out, and it creates balance.

Specific advantages of using whitespace

Easy page reading

content 사이에 공간이 충분하지 않다면, 읽기가 어려워지고 content를 파악하기 위한 노력이 필요하게 된다. Negative space의 밸런스를 맞추고, 특히 micro-space 를 잘 활용하면 reading process가 더 자연스러워질 수 있다. 

Improves the visual hierarchy 

Empty space는 사용자들이 content를 easy-to-read piece로 나눌 수 있게 해주고, detail에 집중할 수 있게 도와준다. 이것은 아티스트들이 stage에서 performance를 잠시 멈출 때와 유사한데, 리스너에게 파악하고 이해할 시간을 주는 것이다.

Design composition does not feel cluttered

Element들 사이의 여백이 충분히 확보되기 때문에 어수선하게 보이지 않는다. 즉, whitespace는 content의 easy accessibility와 ux를 향상시키는 데에 아주 좋은 도구이다.

Focuses the user's attention on the main elements

White space는 사용자의 눈이 search bar나 company logo에 집중하게 해준다. Whitespace의 도움으로 web page의 구조적 스트레스를 줄이고, 중요한 정보에 주의를 집중하게 할 수 있다. 

Adds style and elegance to the page 

White space 자체로 디자인 스타일을 unique하게 만들 수도 있다. White space를 활용한 디자인이 user에게 기억되어, 다른 경쟁사 사이트와의 구별점을 만들어내는 요소가 되기도 한다. 

Empty space는 그저 빈 공간이 아닌, 강력한 디자인 툴이다. 좋은 레이아웃을 만들기 위한 빈 공간 배치는 연습을 충분히 해야겠지만, 많이 만들어볼수록 많이 배우게 될 것이다. 

 

*위 포스트는 아래의 링크를 요약, 정리하였습니다.

link.medium.com/64PJsHiGY8

728x90

'Front-end > UIUX' 카테고리의 다른 글

OK 버튼은 왜 오른쪽에 있을까?  (0) 2021.03.13

+ Recent posts