반응형을 위한 기준 정리

반응형 개발을 할 때에 일반적으로 스크린의 사이즈를 기반으로 한다. 하지만 실제로 개발을 하다보면 가로폭 사이즈 이외에도 고려해야할 점들이 많이 있다는 것을 깨닫게 된다. 반응형 사이트를 개발하면서 나름대로 사용하는 초보적인 수준의 팁들을 정리해볼까 한다.

윈도우 폭

반응형 사이트를 만들 때에 그리드디자인은 거의 필연적으로 취하게 된다. 따라서 그리드&반응형 라이브러리를 찾게 되는데, 나는 몇 년 전부터 지금까지 꽂꽂하게 부트스트랩을 고수하고 있다. 다른 라이브러리들도 그렇겠지만 부트스트랩도 기본적으로 폰, 타블렛, 데스크탑을 제공하는데, 여기에서 폰의 범위가 지나치게 넓다. 따라서 한 손안에 쥐어지는 일반적인 폰의 크기를 위해서 eXtra eXtra Small 사이즈(480px)를 별도로 만드는 것이 좋다.

.col-xxs-1,.col-xxs-10,.col-xxs-11,.col-xxs-12,.col-xxs-2,.col-xxs-3,.col-xxs-4,.col-xxs-5,.col-xxs-6,.col-xxs-7,.col-xxs-8,.col-xxs-9{min-height:1px;padding-left:15px;padding-right:15px;position:relative}.visible-xxs,.visible-xxs-block,.visible-xxs-inline,.visible-xxs-inline-block{display:none!important}@media (max-width:480px){.col-xxs-1,.col-xxs-10,.col-xxs-11,.col-xxs-2,.col-xxs-3,.col-xxs-4,.col-xxs-5,.col-xxs-6,.col-xxs-7,.col-xxs-8,.col-xxs-9{float:left}.col-xxs-1{width:8.333333333333332%}.col-xxs-2{width:16.666666666666664%}.col-xxs-3{width:25%}.col-xxs-4{width:33.33333333333333%}.col-xxs-5{width:41.66666666666667%}.col-xxs-6{width:50%}.col-xxs-7{width:58.333333333333336%}.col-xxs-8{width:66.66666666666666%}.col-xxs-9{width:75%}.col-xxs-10{width:83.33333333333334%}.col-xxs-11{width:91.66666666666666%}.col-xxs-12{width:100%}.col-xxs-push-1{left:8.333333333333332%}.col-xxs-push-2{left:16.666666666666664%}.col-xxs-push-3{left:25%}.col-xss-push-4{left:33.33333333333333%}.col-xxs-push-5{left:41.66666666666667%}.col-xxs-push-6{left:50%}.col-xxs-push-7{left:58.333333333333336%}.col-xxs-push-8{left:66.66666666666666%}.col-xxs-push-9{left:75%}.col-xxs-push-10{left:83.33333333333334%}.col-xxs-push-11{left:91.66666666666666%}.col-xxs-pull-1{right:8.333333333333332%}.col-xxs-pull-2{right:16.666666666666664%}.col-xxs-pull-3{right:25%}.col-xxs-pull-4{right:33.33333333333333%}.col-xxs-pull-5{right:41.66666666666667%}.col-xxs-pull-6{right:50%}.col-xxs-pull-7{right:58.333333333333336%}.col-xxs-pull-8{right:66.66666666666666%}.col-xxs-pull-9{right:75%}.col-xxs-pull-10{right:83.33333333333334%}.col-xxs-pull-11{right:91.66666666666666%}.col-xxs-offset-1{margin-left:8.333333333333332%}.col-xxs-offset-2{margin-left:16.666666666666664%}.col-xxs-offset-3{margin-left:25%}.col-xxs-offset-4{margin-left:33.33333333333333%}.col-xxs-offset-5{margin-left:41.66666666666667%}.col-xxs-offset-6{margin-left:50%}.col-xxs-offset-7{margin-left:58.333333333333336%}.col-xxs-offset-8{margin-left:66.66666666666666%}.col-xxs-offset-9{margin-left:75%}.col-xxs-offset-10{margin-left:83.33333333333334%}.col-xxs-offset-11{margin-left:91.66666666666666%}.visible-xxs{display:block!important}table.visible-xxs{display:table}tr.visible-xxs{display:table-row!important}td.visible-xxs,th.visible-xxs{display:table-cell!important}.visible-xxs-block{display:block!important}.visible-xxs-inline{display:inline!important}.visible-xxs-inline-block{display:inline-block!important}.hidden-xxs{display:none!important}}

윈도우 비율

반응형에서 보통 높이값은 중요하지 않게 취급된다. 그리고 정말 그렇다. 왜냐하면 세로로는 컨텐츠가 길어지는 경우 자연스럽게 스크롤되는 영역으로 인식이 되고 스크롤은 터치디바이스에서 너무 자연스럽기 때문이다.

다만 윈도우의 폭과 높이의 비율은 나름 유용하게 사용될 때가 있다. 이를테면 웹앱의 형태로 하단에 탭바를 넣는 경우를 생각해 볼 수 있다. 문제는 폭을 기준으로만 해서 탭바를 넣으면, 가상키보드가 활성화 되었을 때 그 좁은 화면에도 계속 탭바가 유지되게 된다. 이럴 때 화면의 비율에 따라서 탭바가 나올 수 있는 상황을 제어할 수 있다.

@media screen and (max-aspect-ratio: 1/1) {
/* 높이가 폭보다 더 길때에만 해당된다. */
}

터치기준

기본적으로 모바일은 터치기반, 데스크탑은 논터치(클릭) 기반으로 볼 수 있다. 하지만 MS서피스나 아이패드3처럼 큰스크린의 타블렛이 나오기 시작하면서 터치나 클릭만으로 모바일과 논터치를 구분하기는 힘들어졌다. 따라서 가로폭과 함께 터치기반의 여부도 반응형 웹사이트를 개발할 때 중요한 이슈가 되었다고 생각한다.

이것에 따라 굳이 최적화를 하지 않아도 큰 사용성에는 문제가 생기지 않는다. 하지만 웹앱의 형태같이 UI가 조금더 최적화 되기 시작하면 애매한 부분들이 생긴다. 이를테면 전통적인 웹에서는 마우스가 올라가 있느냐에 따라(mouseover, :hover) 동작되는 것들이 있었는데, 터치기반에서는 작동되지 않는다.

기능이 동작되지 않는 최악의 상황을 막기 위해서는 mousehover나 :hover등을 아예 사용하지 않고 무조건 클릭(탭) 기반으로 작동되도록 하면 될 것이다. 하지만 마우스는 데스크탑 기반에서는 엄청 유용하기 때문에, 모바일을 위해 지나치게 데스크탑을 희생할 필요는 없다. 단지 그에 따라 최적화를 하면 된다.

Modernizer 등의 툴을 이용하면 디바이스가 터치를 지원하는지 안하는지에 따라서 최상위 태그<html> 에 .touch 혹은 .no-touch 클래스를 붙여준다. CSS를 설계할 때에 이 최상위 클래스 여부에 따라 최적화를 진행할 수 있다.

html.touch {
...
}
html.no-touch {
...
}

가장 손쉽게 떠올릴 수 있는 예로 Bootstrap의 dropdown-menu 가 있을 수 있다.

이렇게 생긴 요소입니다.

기본적으로 아래쪽 화살표가 있는 버튼을 누르면, 하위 메뉴가 나타난다. 모바일에서는 탭하여 하위 메뉴를 열기 때문에 그다지 불편하지 않다. 하지만 마우스 기반에서 이런 경우 수정하려는 컴포넌트에 마우스를 올리는 것만으로 우측 상단에 수정/삭제 등의 버튼이 나타나는 경우가 많다. 마우스를 위하여 아래와 같이 최적화 코드를 추가해줄 수 있다.


html.no-touch .dropdown:hover .dropdown-menu {
top: 0;
display: block;
margin-top: 0;
}

또 다른 예로 버튼이나 리스팅의 기본 높이를 다르게 할 수 있다. 마우스 기반의 데스크탑은 일반적으로 포인팅이 정교하기 때문에 폭이 다소 좁아도 허용이 된다. 하지만 터치 기반의 폰에서는 마우스 기반보다는 폭을 조금 더 넉넉하게 잡아주여야할 필요성이 있다.

a.btn, a.list-group-item {
padding: 8px 12px;
}
html.no-touch a.btn, html.no-touch a.list-group-item {
padding: 12px 12px;
}

디바이스 넓이

반응형으로 했을 때 화면의 사이즈에 따라 CSS를 설정하는 것은 단순하다. 하지만 특정 요소에 이벤트를 걸어주는 경우, 반응형은 다소 골치가 아프다. 이를테면 폰사이즈에서는 버튼이 눌렸을 때 A라는 메서드를 실행하도록 하려고 하는데, 사이즈의 변화에 따라서 이미 바인딩한 이벤트를 넣었다 뺐다가 하는 것은 큰 비용같이 느껴진다. 이런 경우 메서드 자체가 실행되는 것은 당연히 처리하고, 그 코드 내에서 화면의 사이즈를 인식하여 폰이 아닌 경우에는 return false 처리를 할 수도 있다. 그런데 이런식으로 다양한 사이즈/디바이스에 대응하는 이벤트가 모두 바인딩되어 있다면 엄청난 낭비가 된다.

이런 경우 디바이스의 불변하는 스크린사이즈를 고려하여 리사이징이 되더라도 애초에 이 디바이스가 폰크기인지, 데스크탑 크기인지 인지를 할 수 있도록하면 될 것이다. window 객체에 있는 window.screen.widthwindow.screen.height 를 이용하면 디바이스의 본래 크기 자체를 얻어낼 수 있다. 여기에 한 발 더 나아가서 모바일에서 자주 변경되는 portrait 혹은 landscape 의 변화를 막기 위해서 면적을 기준으로 하는 것이 좋다.

const isPhone = window.screen.width * window.screen.height < 500000;

50,000이라는 숫자는 제가 경험적으로 인식하는 일반적인 폰사이즈로 상황에 따라 다를 수 있다. 위와 같이 면적기반으로 폰 여부를 결정하고, 그 결과값에 따라 이벤트 바인딩을 결정하면 화면 사이즈 변경에 따른 불필요한 이벤트 바인딩을 막을 수 있을 것이다.

html.is-phone {
...
}

디바이스 종류

보다 더 세부적으로 경험을 설계하기 위해서는, 각 디바이스별로 아예 다른 변수들을 부여하면 된다. 이게 중요한 이유는 아이폰계와 안드로이드계가 모바일에서 다른 경험들을 제공하고 있기 때문이다(구현에 앞서 모바일웹상에서 네이티브 경험을 복원하는 것이 과연 옳은 것이가에 대해서도 고민을 해볼 필요는 있다).

가장 쉽게 생각해 볼 수 있는 것은, 안드로이드에서는 좌측 엣지 스와이프를 위해서 메인 네비게이션을 호출하는 것이 일반적이다. 이 습관은 너무 강력해서 웹상에서도 좌측네비게이션을 버거메뉴로 감춘 경우 습관적으로 스와이프를 하게 된다. 이 경우 안드로이드에 한정해서 엣지 스와이프 이벤트를 걸어주면 어색함은 누그러질 수 있다.

const isAndroid = /android/i.test(navigator.userAgent);
const isIos = /ipad|iphone|ipod/i.test(navigator.userAgent);

그리고 이러한 변수를 <html>에 클래스로 걸어주어 CSS에서도 활용할 수 있다.

html.is-android {
...
}
html.is-ios {
...
}

Overriding

일반적인 모바일퍼스트로 작업을 진행한다면, 윈도우폭이 커질수록 셀렉트를 재지정하여 속성을 override 하여 적용할 것이다. 혹은 그 외에도 어쨌든 기본 속성들에다가 상황에 따른 속성을 override 해야할 것이다.

하지만 개발을 계속 하다보면 생각보다 마크업 구조가 복잡해져서, 이맘때쯤이면 !important 를 이용하여 뗌빵을하게 된다(혹은 그러고 싶어진다). 하지만 !important너무 강력하게 우선순위를 조절해버리기 때문에, 조금 더 부드러운 방법으로 셀렉터 앞에 body 를 붙여주어서 조정하는 것이 좋다. 이 방법이 좋은 이유는 한 번 더 우선순위를 붙여줄 때 html 까지 붙여줄 수도 있다.

본문에서 각각의 상황에 따라 html.is-phone 과 같이 최상위 태그에 클래스를 부여한 이유는 자연스럽게 override 에 대한 우선순위도 조정하게 되는 효과가 있기 때문이다.

마무리

실질적으로 하나의 웹앱(사이트)를 진행하는 경우에 윈도우 폭 이외에도 위의 구분들이 적용하고 있:다. 웹을 이용하는 디바이스 환경이 다양해짐에 따라 단순히 윈도우의 폭만으로 반응형 화면 설계를 하는 시기는 지난 듯이 보인다. 이러한 단순한 경우들을 조금만 신경쓰면 사용자의 경험은 훨씬 쾌적해질 것이라 생각한다.

Like what you read? Give 홍구 a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.