-2.5 C
New York
토요일, 1월 3, 2026

Buy now

Home Blog Page 276

[쇼핑몰 구축기] 폰트어썸(Font Awesome) 적용 시 woff2관련 404 에러 대처법

이번에는 폰트어썸(Font Awesome) 적용 시 흔히 마주치는 woff2관련 404에러 문제를 어떻게 풀어야 하는지에 대해서 살펴봅니다.

폰트어썸(Font Awesome) 적용 시 woff2관련 404 에러 대처법

쇼핑몰 구축기를 연재하는 이유

최근 지인이 워드프레스를 이용해 쇼핑몰 구축을 시도하면서 배웠던 배웠던 다양한 경험들을 해당 쇼핑몰 블로그에 연재해 왔는데요.

쇼핑몰이 상품만 파는 것이 아니라 쇼핑몰을 방문하는 고객들에게 열가지 유용한 정보를 제공하는 블로그의 효용성이 높다는 점을 십분 활용하고, 처음 시작하는 쇼핑몰의 신뢰성을 주기 위해 비록 삽질이지만 삽질기를 낱낱히 공개하기로 했다고 하네요.

그 쇼핑몰의 주소는  https://puripia.com로 아직도 공사중이기는 합니다.) 

쇼핑몰 구축 시 도와주었던 인연으로 그 쇼핑몰을 알리고 쇼핑몰 구축 경험담을 보다 널리 알리기 위해서 여기 happist.com에도 같이  공유합니다. 조금 사심이 있기는 합니다.

폰트어썸(Font Awesome) 적용 시 woff2관련 404 에러 대처법

이모티콘이나 이모지등이 널리 활성화되면서 웹페이지에서 이러한 경향을 다소 늦었지만 적극적으로 수용하려는 시도가 지속되어 왔습니다.

아이콘을 사용해 보다 직관적으로 사용 경험을 제공할 수 있고, 군더더기 설명을 줄일 수 있어 깔끔한 사이트 구현도 가능합니다.

이렇게 웹 페이지에서 아이콘을 구현하는 여러가지 방법이 있지만 가장 널리 사용되는 것이 바로 폰트어썸 (Font Awesome) 아이콘 폰트입니다.

폰트어썸 (Font Awesome) 공식 페이지 살펴보기

1. 폰트어썸 (Font Awesome) 적용 시 woff2관련 404 에러

폰트어썸 (Font Awesome)이 등장한지 어느 정도 시간이 걸려서 일반적인 웹사이트에서 이를 적용하는 것은 어렵지는 않습니다.

그러나 이를 적용하는 과정에서 아무래도 열가지 장애를 만나게 되는데요.

이 중 오늘 이야기하는 woff2를 표현하지 못하고 404에러를 뿜어내면서 적용되지 못하는 현상을 어떻게 해결할 것인지에 대해 그동란 뻘짓하면서 알아낸 내용을 공유하고자 합니다.

이는 webpagetest.org에서 테스트해보면 다른 항목들은 제대로 작동하는데 woff2는 작동하지 않는 것을 알 수 있습니다.

[쇼핑몰 구축기] 폰트어썸(Font Awesome) 적용 시 woff2관련 404 에러 대처법 1

2. 폰트어썸 (Font Awesome) 적용 시 점검 사항

왜 이런 문제가 발생하는 것일까요?

저도 처음에는 어떻게 접근해야할지 몰라서 너무 당황했고 몇달동안 그냥 무시했습니다.

사이트 운영에서 큰 문제는 없었기 때문에요. 아마 어느 부분이제대로 표현되지 않았을텐데 그냥 감내하기로 한것이지요.

그러는 가운데 이문제를 한번 풀어야겠다고 마음먹고 엄청난 구글링 작업에 들어갔습니다.

2.1. MIME 타입에 woff2가 등록되었는지 확인

그러면서 어쩌면 당연하겠지만 woff2를 MIME 타입에 등록한다는 사실을 알게되었습니다. 초심자라 모르는게 너무 많군요!!!!!!

  • 폰트, 이미지, 텍스트 등 대부분의 형식들은 MIME 타입을 등록합니다.
  • woff2 파일 형식이 등장했던 시기에 이 파일을 woff2를 어떻게 등록할 것인지에 대한 가이드가 많이 있었습니다.

아는 nginx라면 /etc/nginx/mime.types라는 파일에 정의되어 있습니다. 여기에서 woff2가 제대로 등록되어 있는지 확인해보면 됩니다.

대부분 아래처럼 등록이 되어 있습니다.

font/woff   woff;
font/woff2   woff2;
Code language: PHP (php)

2.2. 웹서버 설정 확인 – woff2가 전부 반영되었는지

웹서버 설정에서 별다른 설정을 하지 않았다면 별 문제가 없을지도 모릅니다.

그러나 대부분 내용이 쉽게 변하지않는 파일의 경우 속도 향상을 캐싱하도록 만드는 옵션을 적용하고는 합니다.

아래는 nginx에서 적용했던 옵션인데요.

location ~ \.(?:asf|asx|wax|wmv|wmx|avi|bmp|class|divx|doc|docx|eot|exe|gif|gz|gzip|ico|jpg|jpeg|jpe|mdb|mid|midi|mov|qt|mp3|m4a|mp4|m4v|mpeg|mpg|mpe|mpp|odb|odc|odf|odg|odp|ods|odt|ogg|ogv|otf|pdf|png|pot|pps|ppt|pptx|ra|ram|svg|svgz|swf|tar|t?gz|tif|tiff|ttf|wav|webm|wma|woff|wri|xla|xls|xlsx|xlt|xlw|zip)$ {
    expires 31536000s;
    add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}Code language: PHP (php)

여기를 보시면 woff는 있는데 woff2는 없다는 것을 알 수 있습니다.

이 옵션이 사용 유무를 결정하는 것은 아닐텐데요. 여기에 없다는 이유로 woff2는 작동하지 않더군요.

그래서 여기 리스트에 woff2를 넣어주었습니다. 그랬더니 아무일 없었다는 듯 작동하더군요..

조금 허무했습니다. 최종 적용 옵션입니다.

location ~ \.(?:asf|asx|wax|wmv|wmx|avi|bmp|class|divx|doc|docx|eot|exe|gif|gz|gzip|ico|jpg|jpeg|jpe|mdb|mid|midi|mov|qt|mp3|m4a|mp4|m4v|mpeg|mpg|mpe|mpp|odb|odc|odf|odg|odp|ods|odt|ogg|ogv|otf|pdf|png|pot|pps|ppt|pptx|ra|ram|svg|svgz|swf|tar|t?gz|tif|tiff|ttf|wav|webm|wma|woff|woff2|wri|xla|xls|xlsx|xlt|xlw|zip)$ {
    expires 31536000s;
    include /etc/nginx/security-headers.conf;
Code language: PHP (php)

여기서 include /etc/nginx/security-headers.conf;를 삽입한 이유를 간단히 설명하죠.

이전에 설명한 “[쇼핑몰 보안] NGINX에서 HTTP 보안 헤더(Security Headers) 적용 時 주의점“에서 설명한 내용과 같습니다.

즉 nginx는 보안 헤더관련 명령이 뒤에 나오면 앞에서 설정한 모든 보안 헤더관련 옵션이 무효화됙 때문에 보안 헤더 적용을 파일을 블러오는 것으로 처리했기 때문입니다.

[참고] Apache와 IIS에서 woff2 등록 방법

아마 최근에 설치된 최신 웹서버라면 woff2는 기본으로 등록되어 있겠지만 Apache와 IIS에서 woff2를 등록하는 방법은 아래와 같다고 합니다.

Apache에서 woff2 등록하기

.htaccess 파일에 아래 내용을 추가합니다.

# MIME Mappings
AddType font/woff .woff
AddType font/woff2 .woff2Code language: PHP (php)

IIS에서 woff2 등록하기

IIS 엡서버를 사용하는 경우에는 woff2는 아래와 같은 스니펫(snippet)을 추가해 해결할 수 있습니다.

<configuration>
  <system.webServer>
    <staticContent>
      <mimeMap fileExtension=".woff" mimeType="font/woff" />
      <mimeMap fileExtension=".woff2" mimeType="font/woff2" />
    </staticContent>
  </system.webServer>
</configuration>Code language: PHP (php)

[쇼핑몰 구축기] 속도 개선을 위한 폰트어썸(Font Awesome) 외부 로딩을 금지하는 방법

앞서 이야기한 구글 폰트 로딩을 금지하는 것과 마찬가지로 다양한 아이콘 활용을 위한 폰트어썸(Font Awesome)의 경우도 외부 로딩을 금지해햐 할 경우가 있습니다.

폰트어썸(Font Awesome)가 활발하게 적용되면서 테마 설계 시 폰트어썸(Font Awesome)은 로컬 서버에서 불러오도록 설계하는 경향이 늘고 있습니다. 속도 측면에서 유리하기 때문이죠. 조금 트래픽에서 손해를 보드라도 말입니다.

그런데 그굴폰트와 마찬가지로 플러그인마다 폰트어썸(Font Awesome)를 불러오는 방식이 다릅니다. 플러그인으로서는 테마마다 어떤 방식으로 폰트어썸(Font Awesome)을 불러오늕 모르므로 기본적으로 외부에서 불러오는 방법을 택합니다.

그러다보면 로컬에서 폰트어썸(Font Awesome)을 불러오고 또 외부에서 폰트어썸(Font Awesome)을 불러오는 이중의 상황이 발생합니다.

이를 어떻게 막을 수 있을까요?

사이트 속도 개선을 위해 폰트어썸(Font Awesome) 외부 로딩을 금지하는 방법

쇼핑몰 구축기를 연재하는 이유

최근 지인이 워드프레스를 이용해 쇼핑몰 구축을 시도하면서 배웠던 배웠던 다양한 경험들을 해당 쇼핑몰 블로그에 연재해 왔는데요.

쇼핑몰이 상품만 파는 것이 아니라 쇼핑몰을 방문하는 고객들에게 열가지 유용한 정보를 제공하는 블로그의 효용성이 높다는 점을 십분 활용하고, 처음 시작하는 쇼핑몰의 신뢰성을 주기 위해 비록 삽질이지만 삽질기를 낱낱히 공개하기로 했다고 하네요.

그 쇼핑몰의 주소는  https://puripia.com로 아직도 공사중이기는 합니다.) 

쇼핑몰 구축 시 도와주었던 인연으로 그 쇼핑몰을 알리고 쇼핑몰 구축 경험담을 보다 널리 알리기 위해서 여기 happist.com에도 같이  공유합니다. 조금 사심이 있기는 합니다.

사이트 속도 개선을 위해 폰트어썸(Font Awesome) 외부 로딩을 금지하는 방법

여러번 언급했지만 소셜 미디어나 제반 프로그앰등에서 이모지나 아이콘등이 활발하게 사용되면서 이 분야에서 상대적으로 보수적으로 접근했던 웹에서도 적극적으로 이러한 아이콘을 도입하고 있습니다.

그런데 워드프레스은 통일된 하나의 프로그램이 아니라 워드프레스 core에 각종 플러그인을 추가해 프로그램을 완성하는 방법을 사용하다보니 예상치 못했던 부작용을 만날 수 있습니다.

폰트어썸(Font Awesome)관 관련해서 다양한 적용방법이 있다보니 각 플러그인들이 독자적으로 폰트어썸(Font Awesome)을 적용하면서 이중으로 적용하는 문제가 발생하기도하죠.

예를들어 OceanWP테마는 속도를 고려해 서버 로컬에서 폰트어썸(Font Awesome)관련 콘텐츠를 끌어오도록 설계되어 있습니다. 그런데 POPUP Login 기능을 제공하는 플러그인중에서 자체적으로 외부의 폰트어썸(Font Awesome) 파일을 로딩하도록 설정하고 있습니다.

그렇기때문에 로컬에서 불러오는 폰트어썸(Font Awesome)과 외부 예를들어 “https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css”가 동시에 브러들이게 되는 상황이 발생합니다.

1. 폰트어썸(Font Awesome) 외부 로딩은 무엇이 문제일까?

외부에 있는 폰트어썸(Font Awesome)을 불러오는 것은 편리하기는 하지만 상대적으로 속도를 느리게 만듭니다.

외국의 사례는 전체 로딩 시간의 1/3을 차지하는 경우까지도 있다고 합니다.

아래는 ttps://use.fontawesome.com/releases/v5.5.0/css/all.css?ver=5.2.1을 로딩하기위해서 1.047초가 걸렸고 다른 차일들이 로딩하는 것을 막고 있는 상황을 잘 보여주고 있습니다.

[쇼핑몰 구축기] 속도 개선을 위한 폰트어썸(Font Awesome) 외부 로딩을 금지하는 방법 2

2. 어떻게 폰트어썸(Font Awesome) 외부 로딩을 막을까?

그러면 어떻게 이럴게 속도를 지연시키는 폰트어썸(Font Awesome) 외부 로딩을 막을 수 있을까요?

2.1. filter 이용법 01

차일드 테마의 functions.php에서 원하지 않는 CSS 파일을 로딩하지 않토록 만드는 방법입니다.

이 명령의 경우 먹히는 경우도 있고 아닌 경우도 있다고하니 실험해보시고 안된다면 다른 대안을 찾아야 할 것 같아요.

예를 들어 새롭게 설치한 플러그인에서 폰트어썸(Font Awesome)을 “https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css”에서 로딩한다고 할때, 로딩을 막는 방법을 찾아보도록 하겠습니다.

이는 stackoverflow.com에서 소개한 방법인데요.

function remove_unwanted_css(){
wp_dequeue_style('font-awesome', 'https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css');
}
add_filter('wp_print_styles', 'remove_unwanted_css');Code language: PHP (php)

2.2. 2.1. filter 이용법 02

마찬가지로 차일드 테마의 functions.php에서 스타일 적용을 무력화 시키는 방법인데요.

플러그인에 아래와 같이 스타일이 적용되어 있다면

wp_enqueue_style( 'sb-font-awesome', 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css', array(), '4.7.0' );Code language: PHP (php)

이는 플러그인에서 정의한 스타일을 적시하고 이를 무력화하도록 만드는 명령을 추가하는 것입니다.

wp_dequeue_style( 'sb-font-awesome' );
wp_deregister_style( 'sb-font-awesome' );Code language: PHP (php)

또는 액션 명령으로 만들 수도 있습니다. “How to Remove Font Awesome from WordPress Theme” 참조

add_action( 'wp_print_styles', 'tn_dequeue_font_awesome_style' );
function tn_dequeue_font_awesome_style() {
      wp_dequeue_style( 'sb-font-awesome' );
      wp_deregister_style( 'sb-font-awesome' );
}Code language: PHP (php)

이 방법은 간단항것 같지만 플러그인에서 사용하는 스타일 이름과 테마에서 사용하는 스타일 이름이 같으면 별 도움이 안되는 듯 합니다.

2.3. Content-Security-Policy 활용

원래 이 Content-Security-Policy의 목적은 이게 아니었지만 제대로 활용하면 효과적인 제어 장치가 됩니다.

Content-Security-Policy에서는 등록된 화이트 리스트(White list)만 로딩되고 작동하기 때문에 등록 화이트 리스트(White list)에서 제외하면 외부의 어떤 소스도 작동할 수 없습니다.

이를 응용하면 구글폰트의 외부 로딩으로 고민하는데 여기에서 원천적으로 막을 수 있습니다.

저의 경우 Content-Security-Policy의 style-src를 아래와 같이 세팅했습니다.

여러 유명회사들이 들어가 있는 이유는 이런 저런 서비스를 이용하다보면 이들 회사것을 알게 모르게 사용하게 때문에 화이트 리스트(White list)에 올려 놓았습니다.

add_header Content-Security-Policy "style-src 'self' 'unsafe-inline' 'unsafe-eval' *.google.com *.twitter.com *.facebook.com  *.google-analytics.com *.daumcdn.net *.daum.net *.gstatic.com *.naver.net *.naver.com *.inicis.com ;" always;Code language: PHP (php)
Content-Security-Policy 활용 폰트어썸 로딩 금지

[쇼핑몰 구축기] 속도 개선을 위한 구글 폰트 로딩 방지법

앞서 가장 효과적으로 웹폰트를 적용하는 방법에 대해서 살펴 보았습니다. 그러한 방법을 통해서 자신이 원하는 웬폰트 중심으로 콘텐츠가 보여지도록 설정했죠.

그럼에도 불구하고 대부분의 테마나 플러그인은 대부분 구글 웬폰트를 사용하도록 프로그램되어 있다보니 자동으로 구글 웬폰트를 로딩합니다. 이는 불필요하게 속도를 저하시키고 슬데없는 트래픽을 발생시킵니다.

여기에서는 속도와 트래픽에 좋지않은 영향을 미치는 구글 폰트가 로딩되지 못하도록 막는 방법에 대해서 살펴봅니다.

[쇼핑몰 속도 개선] 구글 폰트 로딩 방지법

쇼핑몰 구축기를 연재하는 이유

최근 지인이 워드프레스를 이용해 쇼핑몰 구축을 시도하면서 배웠던 배웠던 다양한 경험들을 해당 쇼핑몰 블로그에 연재해 왔는데요.

쇼핑몰이 상품만 파는 것이 아니라 쇼핑몰을 방문하는 고객들에게 열가지 유용한 정보를 제공하는 블로그의 효용성이 높다는 점을 십분 활용하고, 처음 시작하는 쇼핑몰의 신뢰성을 주기 위해 비록 삽질이지만 삽질기를 낱낱히 공개하기로 했다고 하네요.

그 쇼핑몰의 주소는  https://puripia.com로 아직도 공사중이기는 합니다.) 

쇼핑몰 구축 시 도와주었던 인연으로 그 쇼핑몰을 알리고 쇼핑몰 구축 경험담을 보다 널리 알리기 위해서 여기 happist.com에도 같이  공유합니다. 조금 사심이 있기는 합니다.

[쇼핑몰 속도 개선] 구글 폰트 로딩 방지법

워드프레스의 대부부능 테마나 플러그인들은 기본적으로 구글 폰트를 사용하는 것을 전제로 프로그램됩니다. 더우기 워드프레스 자체도 구글 폰트 사용을 당연시하고 구글 폰트 로더를 기본으로 제공하고 있기도 하죠.

그러나 구글 폰트는 구글 서버에서 폰트를 가져오고, JS를 사용하므로 중간에 다른 콘테츠 로딩을 막으면서 속도를 저하시키는 원인이 되기도 합니다.

구글 폰트를 적극적으로 사용한다면 큰 문제는 없지만 구글 웹폰트를 사용하지 않는다면 구글 폰트관련 설정 파일들이 로딩되는 것을 막아 최적화할 필요는 있습니다.

1. 구글 폰트 미적용시에도 설정은 로딩된다.

많은 테마에서는 구글 폰트를 사용하지 않는 옵션을 제공합니다.

그러나 이 옵션을 적용해도 다른 플러그인등에서 구글 폰트를 사용하도록 되어 있어서인지 모르지만 구글 폰트 관련 제반 파일들이 로딩됩니다.

https://fonts.googleapis.com/css?....Code language: PHP (php)

이렇게 구글 폰트 관련 설정 내용들이 로딩되면서 테ㅏ에 따라서 50k ~ 250k정도의 용량을 다운받더군요. 대세에 큰 지장은 없지만 일정정도 속도 지연도 발생합니다.

2. 구글 폰트 로딩을 근본적으로 막기 – 플러그인 이용

그러면 구글 폰트를 사용하지 않을 시 구글 폰트 설정 및 폰트들이 로딩되고 다운받아지는 현상을 어떻게 막을 수 있을까요?

구글링을 통해서 구를폰트 로딩을 막는 다양한 코드들을 테스트해 보았지만 효과가 없었습니다.

또한 구글 폰트 사용을 막아준다는 플러그인들이 몇가지 존재합니다.

아래는 “Disable Google Fonts”라는 키워드로 검색한 구글 폰트관련 플러그인들입니다.
Autoptimize와 Disable Google Fonts와 같은 플러그인들이 보이는군요. 그러나 이중에서 제대로 효과를 내는 것은 딱 하나만 보았습니다.

Disable Google Fonts로 검색한 구글 폰트관련 플러그인들

2.1. Autoptimize 플러그인

Autoptimize 플러그인은 다양한 기능과 함께 CSS 파일들을 묶어 다운할 수 있도록 만들어 서버 요청 회수(request)를 획기적으로 줄여줍니다. 저의 경우 106개 정도의 request가 40여개로 줄더군요, 

그러나 서버 요청 회수(request)를 획기적으로 줄어 들어도 전체 로딩 시간은 별 차이가 없더군요. 조금 아이러니하기는 했습니다.

또한 이 플러그인은 Elementor와 궁합이 맞지 않은 것 같았습니다. 이 플러그인을 적용하면 Elementor로 편집한 화면은 설정이 엉키더군요.

예를 들어 메인 페이지 Link color를 화이트로 설정해 놓았는데 플러그인 적용후에는 블랙으로 바뀌어 버립니다. 그리고 다시 변경하려면 또 디자인이 틀어져 버립니다.

또한 이 플러그인은 구글 폰트 사용을 막아주기는 하지만 구글 폰트관련 설정이 로딩되는 것을 막지는 못하더군요. 그래서 지웠죠.

2.2. Disable Google Fonts

Disable Google Fonts 플러그인은 아준 단순하게 구글 폰트 사용만을 막아줍니다.

사용 자체는 막아주지만 구글 폰트와 관련된 설정이 로딩되는 것을 막지는 못합니다.

그래서 이 플러그인도 삭제될 운명에 처했습니다.

2.3. Remove Google Fonts References

Remove Google Fonts References 플러그은 다소 덜 알려져 있지만 제가 테스트했을 시 확실하게 효과를 보여준 플러그인입니다.

“[워드프레스 Tips] 워드프레스에 설치된 구글웹폰트 제거 방법 – 매뉴얼 제거 또는 플러그인 사용”라는 글에서도 이 플러그인을 추천하고 있더군요.

다만 이 플러그인은 지난 3년간 업데이트가 없었습니다. 이는 조금 불안한 요소로 보입니다.

Remove Google Fonts References 적용 후 속도

Remove Google Fonts References 르 적용 후 전체 로딩 속도가 크게 개선된 것은 아닙니다.

왜냐하면 이전에도 구글 폰트 로딩 용량과 숫자가 많지는 않았기 ㄸ문입니다. 구글 폰트 수가 많고 많은 용량을 다운 받는 경우 효과는 클 것으로 보입니다.

아무튼 Remove Google Fonts References 적용 후 테스트해보니 속도도 괜찮게 나왔고 더우기 그글 폰트와 관련된 설정 파일 로딩도 없어졌습니다.

Web Page Performance Test for puripia.com

[쇼핑몰 구축기] 속도 개선을 위한 구글 폰트 로딩 방지법 3

3. Content-Security-Policy 활용

Content-Security-Policy를 적용하면 등록된 화이트 리스트(White list)만 로딩되고 작동하기 때문에 등록 화이트 리스트(White list)에서 제외하면 외부의 어떤 소스도 작동할 수 없다는 점을 이용하는 것입니다.

이를 응용하면 구글폰트의 외부 로딩을 원천적으로 막을 수 있습니다.

저의 경우 Content-Security-Policy의 style-src를 아래와 같이 세팅했습니다.

여러 유명회사들이 들어가 있는 이유는 이런 저런 서비스를 이용하다보면 이들 회사것을 알게 모르게 사용하게 때문에 화이트 리스트(White list)에 올려 놓았습니다.

add_header Content-Security-Policy "style-src 'self' 'unsafe-inline' 'unsafe-eval' *.google.com *.twitter.com *.facebook.com  *.google-analytics.com *.daumcdn.net *.daum.net *.gstatic.com *.naver.net *.naver.com *.inicis.com ;" always;Code language: PHP (php)

[쇼핑몰 구축기] 가장 효과적인 웹폰트 적용법 – 나눔바른고딕을 중심으로

워드프레스 쇼핑몰이든 워드프레스 블로그 사이트든 사이트 콘텐츠를 가장 미려한 폰트를 이용해 보여주기 위해서는 웹폰트를 사상할 수 밖에 없는데요.

여기에서는 한글 웬폰트를 적용하면서도 속도나 트래픽을 최소로 사용하는 방법에 대해서 살펴봅니다.

[사용 경험 개선] 가장 효과적인 웹폰트 적용법 – 나눔바른고딕을 중심으로

쇼핑몰 구축기를 연재하는 이유

최근 지인이 워드프레스를 이용해 쇼핑몰 구축을 시도하면서 배웠던 배웠던 다양한 경험들을 해당 쇼핑몰 블로그에 연재해 왔는데요.

쇼핑몰이 상품만 파는 것이 아니라 쇼핑몰을 방문하는 고객들에게 열가지 유용한 정보를 제공하는 블로그의 효용성이 높다는 점을 십분 활용하고, 처음 시작하는 쇼핑몰의 신뢰성을 주기 위해 비록 삽질이지만 삽질기를 낱낱히 공개하기로 했다고 하네요.

그 쇼핑몰의 주소는  https://puripia.com로 아직도 공사중이기는 합니다.) 

쇼핑몰 구축 시 도와주었던 인연으로 그 쇼핑몰을 알리고 쇼핑몰 구축 경험담을 보다 널리 알리기 위해서 여기 happist.com에도 같이  공유합니다. 조금 사심이 있기는 합니다.

[사용 경험 개선] 가장 효과적인 웹폰트 적용법 – 나눔바른고딕을 중심으로

쇼핑몰이든 블로그 사이트이든 우선 보기에 좋아야하기 때문에 사용자들이 좋아할 웹폰트를 적용하게 됩니다.

여기서는 웹폰트를 적용하되 가장 효율적으로 그리고 가장 빠른 속도를 낼 수 있는 방안에 대해서 살펴 보겠습니다.

웹폰트 적용에 대해서는 많은 글들이 있지만 아래 글을 참조했고, 동의를 얻어 일부 내용을 가져왔습니다.

[Tips] 본고딕(노토산스CJK) 경량화 버젼 적용법

1. 폰트 적용 방향

어떠한 폰트를 보여주느냐는 사이트 운영 철학 또는 브랜드 정책에 따라 달라집니다.

예를 들어 회사에서 브랜드 홍보용으로 특별히 만든 폰트가 있다면 기본적으로 이 회사 개발 폰트가 가능하면 적용해야 합니다. 그래야 회사의 일관된 메세지가 잘 전달 될 수 있을 것이니깐요.

저희 사이트는 아직 특정 폰트만을 고집할 필요는 없다는 생각했습니다. 사용자들이 어느 정도 좋아할만한 폰트라면 별 문제가 없을 것이죠.

그래서 아래와 같은 원칙을 적용했습니다.

  • 폰트는 사용자들에게 어느 정도 호응을 얻은 폰트라면 적용 가능
  • 애플 기기 사용자들은 애플 SD 산돌고딕 Neo, 윈도우즈 사용자는 나눔바른고딕, 본고딕을 기본적으로 추천
  • 사이트 속도를 최적화하기 위해서 가능하면 사용자의 시스템에 설치된 폰트를 사용토록 유도
  • 시스템폰트에 원하는 폰트가 없다면 나눔바른고딕을 다운, 적용

2. 웹폰트 적용법

위에서 정한 원칙에 따라 아래의 포인트들을 점검했습니다.

2.1. 사용자 PC의 시스템 폰트 활용

사용자의 시스템에 있는 폰트를 이용하려면 local()이라는 명령을 사용합니다.

애플 사용자라면 맥킨토시 컴퓨터 시스템안에 애플 SD 산돌고딕 Neo이 설치되어 있을 것이고, 위도우즈 사용자라면 나눔바른고딕이나 본고딕 또는 최소한 맑은고딕체가 설치되어 있을 것입니다.

사이트 메인 페이지가 로딩되면서 이러한 폰트를 찾아서 띄워주는 것이죠.

그런데 이 시스템에 설치된 폰트이름이 다른 경우가 많으므로 가장 일반적으로 사용하는 이름들은 전부 적용해 줍니다.

  • 나눔 바른 고딕 : 나눔바른고딕, NanumBarunGothic, 나눔바른고딕OTF, NanumBarunGothicOTF (NanumBarunGothicOTF와 나눔바른고딕OTF는 맥용 safari, Firefox, Chrome에서 필요)
  • 애플 SD 산돌고딕 Neo : ‘애플 SD 산돌고딕 Neo’, ‘Apple SD Gothic Neo’
  • 맑은고딕 : ‘맑은 고딕’, ‘Malgun Gothic’
  • 나눔고딕 : 나눔고딕, NanumGothic

2.2. 나눔바른고딕 웹폰트 적용

위에서 설정한 폰트들이 없는 경우 나눔바른폰트를 내려받도록 합니다.

  • IE8이하 브라우저들은 EOT 파일을 내려 받도록 합니다.
src: url('https://puripia.com/Font/nanumbarungothicregular.eot'); /*For IE6~8*/Code language: PHP (php)
  • IE 6~8은 EOT 파일만 지원하고 포맷명을 해석하지 못하기 때문에 파일명뒤에 물을표(?)를 추가하면 물음표 이후 구문을 모두 쿼리문으로 인식해 무시하도록 합니다. 관련 추가 명령을 추가합니다.
url('https://puripia.com/Font/nanumbarungothicregular.eot?#iefix') format('embedded-opentype'),Code language: PHP (php)
  • IE9+이상은 용량이 조금 크지만 가독성이 좋은 WOFF를 다운받도록 한다면 아래 코드로 대신 합니다.
 src: local(☺),Code language: PHP (php)
  • WOFF 포맷을 30% 더 압축해 용량을 줄인 WOFF2를 지원하는 브라우저는 WOFF2를 내려 받도록 먼저 위치시킵니다. Chrome, Fire Fox, Opera, Edge가 지원합니다
url('https://puripia.com/font/nanumbarungothicregular.woff2') format('woff2'),Code language: PHP (php)
  • 나머지 모던 브라우저들은 WOFF 포맷을 지원하므로 이를 다운토록 합니다. WOFF를 지원하는 IE9+, Chrome, Safari, Opera 등등 대부분이 지원하지만 압축율이 좋은 WOFF2를 지원하는 Chrome, Fire Fox, Opera, Edge를 빼고 IE9이상, Safari에서 적용됩니다.
url('https://puripia.com/font/nanumbarungothicregular.woff') format('woff'),Code language: PHP (php)
  • WOFF 포맷이 적용안되는 경우는 이전부터 사용되던 TTF를 다운하도록 합니다. TTF형식은 용량도 크고 쉽게 복제 가능해 이를 대체코자 WOFF가 만들어졌습니다.
url('https://puripia.com/font/nanumbarungothicregular.ttf') format('truetype'),Code language: PHP (php)
  • WOFF나 TTF 포맷을 지원하지 않은 구 모바일 브라우져(Safari 4.3 이하, Android 4.3 이하, Opera Mobile 10 이하 등)를 위해서 CSS2에서 폰트로 활용 가능한 SVG(Scalable Vector Graphics)를 적용합니다.
url('https://puripia.com/Font/notokr-demilight.svg') format('svg');Code language: PHP (php)

3. 폰트 적용 위치 지정

다음으로는 이 폰트를 어디에다 적용할지를 정해줍니다.

대부분은 body를 비롯해 모든 장소에 이 폰트를 적용하는 것으로 지정하죠. 사용위치 그리고 font-family를 정의합니다.

  • 테마의 css 파일에서 지정하는 것들을 무시하고 싶다면 font-family 마지막 부분에 !important를 추가합니다.
  • font-family 마지막 부분에 !important를 넣었다면 아마 대부분 FontAwesome 폰트가 적용되지 않습니다. 이를 해결하려면 별도 명령어를 추가해야 합니다.
  • 테마의 css 파일에서 지정한 명령이 어느 정도도 필요하다면 font-family 마지막 부분에 !important를 적용하지 않습니다. 그러면 FontAwesome 폰트도 적용되는데요. 저는 Edge 등에서 일부 글꼴이 적용되지 않는 현상이 발견되더군요.

3.1. font-family 마지막에 !important 미지정 시

저는 아래와 같이 지정했습니다.

html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video 
 {
    font-family: '애플 SD 산돌고딕 Neo','Apple SD Gothic Neo','Helvetica Neue','Helvetica','나눔바른고딕','NanumBarunGothic','나눔바른고딕OTF','NanumBarunGothicOTF','Noto Sans','Noto+Sans','notokr-demilight','나눔고딕','NanumGothic','맑은 고딕','Malgun Gothic','sans-serif';
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  vertical-align: baseline;
 }Code language: PHP (php)

이렇게 적용 시 저의 경우는 Edge나 IE에서는 우커머스 부분에서 일부 제목 폰트가 적용되지 않더군요.

3.2. font-family 마지막에 !important 지정 시

가끔 폰트 적용 위치를 지정했음에도 불구하고 폰트가 제대로 나오지 않은 경우가 있습니다. 그러는 경우는 font-family 끝에 !important를 붙여줍니다.

이 경우 강제로 모든 경우 이 폰트로 적용해 버리기 때문에 FontAwesome 적용이 안되는 경우가 많습니다.

요즘 FontAwesome 폰트 적용이 많이 늘고 있기 때문에 필히 해결해야하는 문제인데요. 저는 아래와 같은 코드를 추가해서 해결했습니다.

.fa {
	font-family: 'FontAwesome' !important;Code language: PHP (php)

아래는 최종적으로 확정, 적용한 코드입니다.

html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video 
 {
    font-family: '애플 SD 산돌고딕 Neo','Apple SD Gothic Neo','Helvetica Neue','Helvetica','나눔바른고딕','NanumBarunGothic','나눔바른고딕OTF','NanumBarunGothicOTF','Noto Sans','Noto+Sans','notokr-demilight','나눔고딕','NanumGothic','맑은 고딕','Malgun Gothic','sans-serif' !important;
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  vertical-align: baseline;
 }

.fa {
  font-family: 'FontAwesome' !important;
Code language: PHP (php)

4. 최종 적용 코드

아래는 최종적으로 나눔바른고딕체를 적용하기 위해서 적용한 코드이니 참고하시기 바랍니다. 적어도 이 OceanWP 테마에서는 잘 작동합니다.

 @charset “utf-8″
 @font-face {
 font-family: '나눔바른고딕';
 font-style: normal;
 font-weight: normal;
 font-display: swap;
 src: local('나눔바른고딕'),local('NanumBarunGothic'),local('나눔바른고딕OTF'),local('NanumBarunGothicOTF'),local('애플 SD 산돌고딕 Neo'),local('Apple SD Gothic Neo'),local('Noto Sans'),local('Noto+Sans'),local('notokr-demilight'),local('나눔고딕'),local('NanumGothic'),local('맑은 고딕'),local('Malgun Gothic');
 src: url('https://puripia.com/Font/nanumbarungothicregular.eot'); /*For IE6~8*/
 src: local(☺),
     url('https://puripia.com/Font/nanumbarungothicregular.eot?#iefix') format('embedded-opentype'), 
     url('https://puripia.com/font/nanumbarungothicregular.woff2') format('woff2'), 
     url('https://puripia.com/font/nanumbarungothicregular.woff') format('woff'), 
     url('https://puripia.com/font/nanumbarungothicregular.ttf') format('truetype'),
     url('https://puripia.com/Font/notokr-demilight.svg') format('svg');
 }
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video 
 {
    font-family: '애플 SD 산돌고딕 Neo','Apple SD Gothic Neo','Helvetica Neue','Helvetica','나눔바른고딕','NanumBarunGothic','나눔바른고딕OTF','NanumBarunGothicOTF','Noto Sans','Noto+Sans','notokr-demilight','나눔고딕','NanumGothic','맑은 고딕','Malgun Gothic','sans-serif' !important;
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  vertical-align: baseline;
 }

.fa {
  font-family: 'FontAwesome' !important;Code language: PHP (php)

[쇼핑몰 구축기] 보안과 속도를 위해 브로틀리 압축((Brotli Compression)을 Nginx에서 쉽게 이용하는 방법

이번에는 쇼핑몰 전반 콘텐츠를 압축해 속도를 높여주는 압축 프로그램도 보다 보안이 뛰어난 프로그램인 브로틀리 압축((Brotli Compression)을 Nginx에서 쉽게 이용하는 방법에 대해서 살펴 봅니다.

보안과 속도를 위해 브로틀리 압축((Brotli Compression)을 Nginx에서 쉽게 이용하는 방법

쇼핑몰 구축기를 연재하는 이유

최근 지인이 워드프레스를 이용해 쇼핑몰 구축을 시도하면서 배웠던 배웠던 다양한 경험들을 해당 쇼핑몰 블로그에 연재해 왔는데요.

쇼핑몰이 상품만 파는 것이 아니라 쇼핑몰을 방문하는 고객들에게 열가지 유용한 정보를 제공하는 블로그의 효용성이 높다는 점을 십분 활용하고, 처음 시작하는 쇼핑몰의 신뢰성을 주기 위해 비록 삽질이지만 삽질기를 낱낱히 공개하기로 했다고 하네요.

그 쇼핑몰의 주소는  https://puripia.com로 아직도 공사중이기는 합니다.) 

쇼핑몰 구축 시 도와주었던 인연으로 그 쇼핑몰을 알리고 쇼핑몰 구축 경험담을 보다 널리 알리기 위해서 여기 happist.com에도 같이  공유합니다. 조금 사심이 있기는 합니다.

보안과 속도를 위해 브로틀리 압축((Brotli Compression)을 Nginx에서 쉽게 이용하는 방법

여기에서는 보안과 속도 측면에서 좋은 평가를 받고 있는 브로틀리 압축((Brotli Compression)을 Nginx에서 쉽게 이용하는 방법에 대해서 알아 봅니다.

보안과 속도는 상당히 양립하기 어려운 문제죠. 보안을 강화하면서도 속도를 높인다는 것은 쉽지않은 문제이기 때문이죠.

그렇지만 주제가 속도를 빠르게 해주는 솔류션의 문제라면 다르겠죠. 속도를 높여주는 솔류션이 보안과 연계되어 보안을 높이는 요인이 있다면 금상첨화겠죠.

오늘 이야기하려는 브로틀리 압축((Brotli Compression)이 그러한 한 예가 되지않을까 싶습니다.

1. 가장 널리 사용되는 gzip

그동안 서버에서 제반 포맷들을 압축해주는 솔류션으로 gzip이 널리 사용되었습니다.

이 gzip은 대부분의 웹서버가 기본으로 제공하는 기능으로 별다른 조치없이도 적용 옵션만 제대로 명기해 주는 것으로 작동했습니다.

그만큼 안정적이고 적용하기도 어렵지 않았다는 반증이기도 합니다.

그러나 이 gzip은 보안 측면에서는 완벽하지않다는 평가를 받고 있으며, 가능하면 브로틀리 압축((Brotli Compression)으로 대체하라는 주문이 있기도 합니다.

이전에 소개한 “SSL 보안 등급 A+에 도전하는 Let’s Encrypt 인증서 세팅 방법”에서도 잠깐 언급되었던 내용이기도 합니다.

2. 2016년부터 확산되기 시작한 브로틀리 압축((Brotli Compression)

그러는 가운데 구글은 브로틀리 압축((Brotli Compression) 기술을 오픈 소스로 공개했습니다.

그리고 가장 많은 점유율을 가진 크롬이 2016년 5월 27일부터 브로틀리 압축((Brotli Compression)을 지원하기 시작하면서 본격적으로 확산되기 시작했습니다.

지금은 화이어폭스, 마이크로소프트 Edge, 사파리 등 대부분의 주요 브라우져가 이를 지원하고 있습니다. 우리나라에서 아직도 많이 사용하는 IE는 아직 지원하지 않고 있네요.

[쇼핑몰 구축기] 보안과 속도를 위해 브로틀리 압축((Brotli Compression)을 Nginx에서 쉽게 이용하는 방법 4

3. Gzip vs 브로틀리 압축((Brotli Compression)

당연하게도 기존에 널리 사용되었던 gzip과 브로틀리 압축((Brotli Compression) 간의 비교가 관심이 많을 것으로 보입니다.

안전성 여부를 떠나서 압춧 성능과 관련해 비교해 놓은 자료들이 많은데요.

“Text Compression in R: brotli, gzip, xz and bz2 “ 라는 글에서 gzip과 brotli 등 여러 압축 기술들을 평가해 놓은 글을 참조해 봤습니다.

이에 따르면 압축율은 브로틀리 압축((Brotli Compression)이 가장 뛰어납니다.

[쇼핑몰 구축기] 보안과 속도를 위해 브로틀리 압축((Brotli Compression)을 Nginx에서 쉽게 이용하는 방법 5

그러나 압축 속도는 gzip이 브로틀리 압축((Brotli Compression) 보다도 훨씬 뛰어납니다.

[쇼핑몰 구축기] 보안과 속도를 위해 브로틀리 압축((Brotli Compression)을 Nginx에서 쉽게 이용하는 방법 6

여기에서 테스트한 결과를 토대로 정적인 파일(static contents)에는 브로틀리 압축((Brotli Compression)이 뛰어나지만 동적 콘텐츠( on-the-fly)나 스트리밍 데이타에는 적합하지 않다는 평가를 내리고 있습니다.

3. Nginx에서 브로틀리 압축((Brotli Compression) 적용 어려움

종종 언급하지만 nginx는 상당히 장점이 많은 웹서버이지만 때로는 불리한 점도 있습니다.

예를 들어 브로틀리 압축((Brotli Compression)과 같이 새로운 기술의 경우 nginx용으로 개발되어 자동으로 적용되지 않고 별도로 ngix에 맞추어 컴파일을 해줘야 합니다.

이는 nginx 버젼이 바뀌면 이에 맞추어 다시 컴파일해서 세팅해야 한다는 것을 의미합니다. 관리하기 쉽지가 않다는 것이죠.

아래와 같은 글에서 버젼이 바뀌어도 브로틀리 압축((Brotli Compression)을 계속 적용할 수 있다고 하지만 이는 잘 작동하지 않습니다.

아래 글에서 소개하는 방법도 nginx 버젼이 높아지면 별도로 컴파일하는 절차를 다시 진행해야 합니다.

[사이트 속도 개선] 브로틀리 압축(Brotli Compression)으로 속도를 개선해 보기

4. 명령어 하나로 브로틀리 압축((Brotli Compression) 적용하고 데이트하기

그냥 nginx가 업데이트되면 자동으로 브로틀리 압축((Brotli Compression)도 업데이트되면 가장 좋겠지만 이게 가능하지 않으므로 차선으로 쉬운 방법을 찾아 보았습니다.

안벽하지는 않지만 아주 쉽게 자동화할 수 있는 방법이 인터넷에 소개되어 있습니다. 아래 링크를 참조..

Compile Nginx from Source with Brotli Support

여기서 소개하고 있는 brotli.sh 파일을 서버 root에 설치하고 bash 명령어로 실행 시키면 됩니다.

4.1. brotli.sh 만들기

아래처럼 nano와 같은 편집기 명령어를 이용해 brotli.sh 파일을 만듭니다.

nano brotli.shCode language: PHP (php)

이 파일에는 아래 내용을 추가합니다.

#!/usr/bin/env bash

# version 1.2

# use it while developing / testing.
# you may use it in production as well.
# set -o errexit -o pipefail -o noclobber -o nounset
# set -x

# compile Nginx from the official repo with brotli compression

[ ! -d ${HOME}/log ] && mkdir ${HOME}/log

G_DIR="$(pwd)"

# logging everything
log_file=${HOME}/log/brotli.log
exec > >(tee -a ${log_file} )
exec 2> >(tee -a ${log_file} >&2)

# Defining return code check function
check_result() {
    if [ $1 -ne 0 ]; then
        echo "Error: $2"
        exit $1
    fi
}

export DEBIAN_FRONTEND=noninteractive

printf '%-72s' "Updating apt repos..."
sudo apt-get -qq update
echo done.

printf '%-72s' "Installing pre-requisites..."
sudo apt-get -qq install dpkg-dev build-essential zlib1g-dev libpcre3 libpcre3-dev unzip
echo done.

codename=$(lsb_release -c -s)

# function to add the official Nginx.org repo
nginx_repo_add() {
    distro=$(gawk -F= '/^ID=/{print $2}' /etc/os-release)
    if [ "$codename" == "juno" ] ; then
        codename=bionic
    fi

    if [ "$distro" == "elementary" ] ; then
        distro=ubuntu
    fi

    if [ "$distro" == "linuxmint" ] ; then
        distro=ubuntu
    fi

    [ -f nginx_signing.key ] && rm nginx_signing.key
    curl -LSsO http://nginx.org/keys/nginx_signing.key
    check_result $? 'Nginx key could not be downloaded!'
    sudo apt-key add nginx_signing.key &> /dev/null
    check_result $? 'Nginx key could not be added!'
    rm nginx_signing.key

    # for updated info, please see https://nginx.org/en/linux_packages.html#stable
    nginx_branch= # leave this empty to install stable version
    # or nginx_branch="mainline"

    if [ "$nginx_branch" == 'mainline' ]; then
        nginx_src_url="https://nginx.org/packages/mainline/${distro}/"
    else
        nginx_src_url="https://nginx.org/packages/${distro}/"
    fi

    [ -f /etc/apt/sources.list.d/nginx.list ] && sudo rm /etc/apt/sources.list.d/nginx.list
    echo "deb ${nginx_src_url} ${codename} nginx" | sudo tee /etc/apt/sources.list.d/nginx.list
    echo "deb-src ${nginx_src_url} ${codename} nginx" | sudo tee -a /etc/apt/sources.list.d/nginx.list

    # finally update the local apt cache
    sudo apt-get update -qq
    check_result $? 'Something went wrong while updating apt repos.'
}

case "$codename" in
    "stretch")
        nginx_repo_add
        ;;
    "xenial")
        nginx_repo_add
        ;;
    "bionic")
        nginx_repo_add
        ;;
    "juno")
        codename=bionic
        nginx_repo_add
        ;;
     "tara")
        codename=bionic
        nginx_repo_add
        ;;
    *)
        echo "Distro: $codename"
        echo 'Warning: Could not figure out the distribution codename. Continuing to install Nginx from the OS.'
        ;;
esac

sudo install -o ${UID} -g $(id -gn $USER) -d /usr/local/src/${USER}
cd /usr/local/src/${USER}
apt-get source nginx
sudo apt-get build-dep nginx -y

git clone --recursive https://github.com/eustas/ngx_brotli
sudo ln -s /usr/local/src/${USER}/ngx_brotli /usr/local/src/ngx_brotli
sudo chown ${USER}:$USER:$(id -gn $USER) /usr/local/src/ngx_brotli
cd /usr/local/src/${USER}/nginx-*/

# modify the existing config
sed -i -e '/\.\/configure/ s:$: --add-module=/usr/local/src/ngx_brotli:' debian/rules

# if gcc 8 is installed add patch to nginx 
if [ "$(gcc -dumpversion)" == "8" ]; then
    mkdir -p debian/patches
    cp ${G_DIR}/gcc-8_fix.diff debian/patches/gcc-8_fix
    echo "gcc-8_fix" > debian/patches/series
fi

# build the updated pacakge
dpkg-buildpackage -b

# optional
# install the updated package in the current server
cd /usr/local/src/${USER}

# take a backup
[ ! -d ~/backups/ ] && mkdir ~/backups
cp nginx*.deb ~/backups/nginx-$(date +%F)/

# sudo apt-mark unhold nginx
sudo dpkg -i nginx_*.deb

# print info about remove all the sources and apt sources file
cd ~/

printf "
# To clean up after install You can run
 rm -rf /usr/local/src/$(echo ${USER})/nginx*
 rm -rf /usr/local/src/$(echo ${USER})/ngx_brotli
 sudo rm /etc/apt/sources.list.d/nginx.list
 sudo apt-get -qq update
"
# hold the package nginx from updating accidentally in the future by someone else!
sudo apt-mark hold nginx

# stop the previously running instance, if any
sudo nginx -t && sudo systemctl stop nginx

# start the new Nginx instance
sudo nginx -t && sudo systemctl start nginxCode language: PHP (php)

이렇게 만들어진 brotli.sh의 파일 권한을 변경합니다.

chmod +x brotli.shCode language: PHP (php)

이 파일을 아래와 같이 bash 명령을 사용해 실행합니다.

bash britli.shCode language: PHP (php)

그러면 설치 전과정을 알아서 진행합니다. nginx 버젼을 파악해서 말이죠.

nginx 버젼이 변경될때마다 위에서 소개한 bash brotli.sh 명령으로 자동으로 새로 업데이트된 ngix에 맞추어 브로틀리 압축((Brotli Compression)을 적용할 수 있습니다.

[쇼핑몰 구축기] 보안을 위한 HPKP(HTTP Public Key Pinning) 설정법 – NGINX 기준

이번에는 쇼핑몰 보안을 위해 HPKP(HTTP Public Key Pinning)를 웹서버 Nginx를 기준으로 설정하는 방법에 대해서 살펴봅니다.

[쇼핑몰 보안] HPKP(HTTP Public Key Pinning) 설정법 – NGINX 기준

쇼핑몰 구축기를 연재하는 이유

최근 지인이 워드프레스를 이용해 쇼핑몰 구축을 시도하면서 배웠던 배웠던 다양한 경험들을 해당 쇼핑몰 블로그에 연재해 왔는데요.

쇼핑몰이 상품만 파는 것이 아니라 쇼핑몰을 방문하는 고객들에게 열가지 유용한 정보를 제공하는 블로그의 효용성이 높다는 점을 십분 활용하고, 처음 시작하는 쇼핑몰의 신뢰성을 주기 위해 비록 삽질이지만 삽질기를 낱낱히 공개하기로 했다고 하네요.

그 쇼핑몰의 주소는  https://puripia.com로 아직도 공사중이기는 합니다.) 

쇼핑몰 구축 시 도와주었던 인연으로 그 쇼핑몰을 알리고 쇼핑몰 구축 경험담을 보다 널리 알리기 위해서 여기 happist.com에도 같이  공유합니다. 조금 사심이 있기는 합니다.

[쇼핑몰 보안] HPKP(HTTP Public Key Pinning) 설정법 – NGINX 기준

오늘은 쇼핑몰을 비롯해 웨사이트의 보안을 위한 설정 중 HPKP(HTTP Public Key Pinning) 설정 방법에 대해서 살펴보겠습니다.

일반인에 접근하기엔 아주 어려운 개념이라서 저는 방법 소개에 그쳐야 할 것 같아요. 어느 분야든 다 마찬가지이겠지만 이 보안 분야는 특히나 어렵네요.

1. HPKP(HTTP Public Key Pinning)란 무엇인가?

요즘 일반화되고 있는 SSL/TLS 암호화 통신은 상대적으로 중간자 공격(Man In The Middle)에 취약하다고 합니다.

중간에서 인증서를 가로채 악성사이트로 접속시키고도 사용자는 그 사실을 모르는 채 공격을 당할 수 있습니다.

또 보안 장비들은 SSL Decryption 기능을 가지고 중간자(Man In The Middle) 공격 형태로 SSL/TLS 세션을 복호화해 내용을 검증합니다.

즉 보안장비가 SSL/TLS 인증서를 임의로 교쳏 중간에서 데이타를 가로채서 복호화해 검증하는 것이죠. 보안업체가 마음만 먹으면 일개 사이트를 조작하는 것은 아주 쉬운 일이 되겠죠.

그러한 일들을 막기 위해서 SSL/TLS 암호화 통신에 사용할 인증서를 최종 서버의 인증서로 고정(Pinning)해 중간자의 공격이나 보안장비 임의의 인증서를 거부하게 만드는데요.

이를 암호화 통신 인정서 고정이란 의미로 HPKP(HTTP Public Key Pinning)이라고 부릅니다.

HPKP(HTTP Public Key Pinning) 개념도, 이미지 출처 - keycdn.com
HPKP(HTTP Public Key Pinning) 개념도, 이미지 출처 – keycdn.com

2. HPKP(HTTP Public Key Pinning) 생성 방법

그러면 이러한 HPKP(HTTP Public Key Pinning)를 어떻게 생성할 수 있을까요?

2.1. 제공 웹사이트에서 얻기

가장 간단한 방법같은데요. 아래 사이트에 접속해 사이트 주고만 치면 알아서 뽑아줍니다. 금방 뽑아줍니다.

Create your HPKP hash

[쇼핑몰 구축기] 보안을 위한 HPKP(HTTP Public Key Pinning) 설정법 – NGINX 기준 7

아래 사이트는 IP나 사이트 주소를 입력하면 SSL 보안성을 테스트해주고 더 나아가 필요한 HPKP(HTTP Public Key Pinning)도 뽑아줍니다.

시간은 조금 걸립니다. 테스트하고 등등

SSL Security Test

[쇼핑몰 구축기] 보안을 위한 HPKP(HTTP Public Key Pinning) 설정법 – NGINX 기준 8

2.2. 서버에서 직접 뽑아내기

위와 같이 웹사이트에서 뽑아내는 방법도 있지만 조금 찜찌하다면 서버에서 직접 키를 뽑아낼 수 있습니다.

2.2.1. 먼저 프라이빗 키를 백업하자

먼저 프라이빗 키를 백업합니다. 하나는 RSA이고 하나는 Elliptic Curve관련 키입니다.

이를 위해 아래와 같은 openssl 명령을 사용합니다.

openssl req -nodes -sha256 -newkey rsa:4096 -keyout "MyDomain.com.rsa.key" -out "puripia.com.rsa.csr"
openssl req -nodes -newkey ec:<(openssl ecparam -name prime256v1) -keyout "puripia.com.ec.key" -out "MyDomain.com.ec.csr"Code language: PHP (php)

2.2.2.  SPKI 해시 키를 뽑아 내자

다음으로는 SPKI 해시 키를 뽑아 냅니다. 이는 아래와 같은 openssl 명령어를 사용합니다. 이 명령얼 사용해 나온 키를 복사해 놓습니다.

openssl req -pubkey < MyDomain.com.rsa.csr | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64

openssl req -pubkey < MyDomain.com.ec.csr | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64Code language: PHP (php)

2.2.3. Let’s Encrypt  인증 프라이빗 키 생성

이제는 Let’s Encrypt  인증 프라이빗 키를 생성합니다.

여기에서는 ISRG Root X1Let’s Encrypt Authority X3 and Let’s Encrypt Authority X4의 SPKI-hash 값을 뽑아냅니다.

마찬가지로 아래와 같은 opensll 명령을 사용합니다.

curl https://letsencrypt.org/certs/lets-encrypt-x4-cross-signed.pem | openssl x509 -pubkey | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64
curl https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem | openssl x509 -pubkey | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64
curl https://letsencrypt.org/certs/isrgrootx1.pem | openssl x509 -pubkey | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64Code language: PHP (php)

2.2.4. 설정

이제는 각 웹서버에 맞는 형식에 맞추어 위에서 뽑은 다섯개의 키를 적용해 줍니다.

nginx는 아래와 같은 포맷을 사용합니다.

add_header Public-Key-Pins 'pin-sha256="Hash1"; pin-sha256="Hash2"; pin-sha256="Hash3"; pin-sha256="Hash4"; pin-sha256="Hash5"; max-age=60;';Code language: PHP (php)

Apache는 아래와 같은 포맷을 사용합니다.

Header always set Public-Key-Pins "pin-sha256=\"Hash1\"; pin-sha256=\"Hash2\"; pin-sha256=\"Hash3\"; pin-sha256=\"Hash4\"; pin-sha256=\"Hash5\"; max-age=60;";Code language: PHP (php)

2.2.5. 테스트

제대로 작동하는 지 테스트해봐야겠죠?

이는 위에서 소개한 Report URIs HPKP Analyser 에서 테스트해볼 수 있고, SSL Security Test 에서도 테스트해 볼 수 있습니다..

HPKP(HTTP Public Key Pinning) 적용 상태를 테스트한 결과

[쇼핑몰 구축기] HSTS로 보안과 속도 그리고 SEO까지 세마리 토끼를 잡자

쇼핑몰 보안을 위한 설정 중 HSTS를 활용해 보안과 속도 그리고 SEO까지 유리해지는 방법에 대해서 살펴 봅니다.

[쇼핑몰 보안] HSTS로 보안과 속도 그리고 SEO까지 세마리 토끼를 잡자

쇼핑몰 구축기를 연재하는 이유

최근 지인이 워드프레스를 이용해 쇼핑몰 구축을 시도하면서 배웠던 배웠던 다양한 경험들을 해당 쇼핑몰 블로그에 연재해 왔는데요.

쇼핑몰이 상품만 파는 것이 아니라 쇼핑몰을 방문하는 고객들에게 열가지 유용한 정보를 제공하는 블로그의 효용성이 높다는 점을 십분 활용하고, 처음 시작하는 쇼핑몰의 신뢰성을 주기 위해 비록 삽질이지만 삽질기를 낱낱히 공개하기로 했다고 하네요.

그 쇼핑몰의 주소는  https://puripia.com로 아직도 공사중이기는 합니다.) 

쇼핑몰 구축 시 도와주었던 인연으로 그 쇼핑몰을 알리고 쇼핑몰 구축 경험담을 보다 널리 알리기 위해서 여기 happist.com에도 같이  공유합니다. 조금 사심이 있기는 합니다.

[쇼핑몰 보안] HSTS로 보안과 속도 그리고 SEO까지 세마리 토끼를 잡자

요 며칠 사이에 구축하고 있는 쇼핑몰 보안에 대해서 고민하고 있는데요. 오늘은 HSTS에 대한 이야기를 풀어 보도록 하겠습니다.

인터넷이 커지고 특히 이커머스가 커지면서 인터넷 상에서 돈이 될 수 있는 꺼리가 늘었습니다. 그만큼 사기를 치거나 사이트를 공략해 이익을 얻을 수 있는 여지가 커졌습니다.

이러한 가능성은 직설적으로 그만큼 해킹에 대한 유혹이 커졌다고 할 수 있죠.

그렇기에 사이트 또는 쇼핑몰 보안이라는 것이 더욱 더 중요해졌습니다. 잠재적인 해킹 위협에 대응해 다양한 대응 장치를 마련하는 것은 매우 중요합니다.

그런데 이러한 보안 장치는 대부분 시스템 자원을 많이 사용하기에 속도가 느려지고 편리함을 줄이는 경우가 많죠. 느리고 불편하게 만드는 것이 보안의 정석이기는 합니다.

그런데 HSTS라는 녀석은 보안을 강화하면서도 속도를 빠르게 할 수 있고 더욱기 구글과 같은 검색업체의 랭킹을 높여줄 수 있다고하니 엄청 반가운 녀석이라 하지 않을 수 없습니다.

1. HSTS가 무엇인고? 왜 좋다고 하는 것인지?

그러면 HSTS가 무엇인지 간단히 살펴보죠.

HSTS는 HTTP Strict Transport Security의 준말로 브라우저에게 이 사이트는 오직 HTTPS만을 이용해 접속할 수 있다고 알려주는 반응 헤더(response header)라고 합니다.

이게 왜 여러가지로 도움이 되느냐면 가장 높은 점유율을 가지고 있는 크롬은 특정 사이트, 예를 들어 MyDomain.com이 상시적으로 HTTS로만 접속할 수 있다는 것이 보장되면 이를 리스트에 올려 이를 MyDomain.com에 접속할 때는 사용자가 http로 접속하든 https로 접속하든 무조건 https로 연결해 줍니다.

아러한 과정에서 불필요한 과정들이 생략되므로 반응 속도가 높아지고, 이는 상대적으로 빠른 사이트로 인식이 되면서 구글 검색에서의 랭킹이 올라 갈 수 있다고 합니다.

확실하게 HTTPS로 접속하니 보안이 좋아지고, 불필요한 단계를 건너 뛰어 속도가 빨라지고 덩달아 검색 순위도 올라가니 세마리 토끼를 잡을 수 있다고 표현하는 것이죠.

물론 여기에서 얻어지는 속도나 SEO 랭킹이 획기적으로 좋아지지는 않습니다. 그런 마법은 없죠. 다만 미세하지만 보안과 속도 그리고 SEO애 긍정적인 영향을 미친다고 볼 수 있는 방안이라고 할 수 있습니다.

더우기 최근에는 구글 크롬 뿐만이 아니라 파이어폭스 등 다른 주요 브라우저에서도 이런 기능을 적용하고 있습니다.

2. HSTS 조건

그러면 HSTS로 구글에 등록할 수 있는조건은 어떤 것일까요?

구글을 아래와 같은 조건을 내걸고 있습니다.

  • 유효한 인증
  • HTTP에서 HTTPS로 리다이렉트가 같은 호스트에서 이루어 질것
  • 서브 도메인도 모두 HTTPS로 작동해야 함
  • 기본 도메인에 HSTS 헤더를 적용해야 함

아래는 구글 문서 부분을 그대로 캡춰해 공유해 봅니다.

[쇼핑몰 구축기] HSTS로 보안과 속도 그리고 SEO까지 세마리 토끼를 잡자 9

서버에서 HSTS헤더를 적용하는 명령은 아래와 같습니다.

Strict-Transport-Security: max-age=63072000; includeSubDomains; preloadCode language: PHP (php)

3. HSTS 등록하기

위와 같은 조건이 완료되었다면 구글에 HSTS 등록을 합니다.

구글에 HSTS 사이트로 등록하기

여기서 도메일을 입력 후 아래에 있는 check HSTS preload status and eligibility 버튼을 누르면 됩니다.

아직 등록되지 않는 도메인이라면 접수가 되었다는 메세지가 나옵니다.

[쇼핑몰 구축기] HSTS로 보안과 속도 그리고 SEO까지 세마리 토끼를 잡자 10

만약 이미 등록이 되어 있다면 이미 등록도 도메인이라는 메세지가 나오고죠.

마케팅케이스 스터디 관련 글을 주로 올려주는 happist.com의 경우는 아래처럼 나오는군요.

[쇼핑몰 구축기] HSTS로 보안과 속도 그리고 SEO까지 세마리 토끼를 잡자 11

4. 마치며

오늘날에는 파이어폭스, 사파리, 오페라, 마이크로소프트 엣지와 같은 다른 회사의 브라우져들도 구글 크롬이 수집한 preload HSTS 리스트를 활용한다고 합니다.

그러므로 HSTS를 적용하고 preload 리스트에 등록하는 것은 보안을 높이면서도 속도를 빠르게하고 검색에 유리하도록 검색 등급이 올라갈 수 있는 가능성을 높이는 것이므로 적극 적용할 필요가 있습니다.

[쇼핑몰 구축기] 웹서버 NGINX에서 HTTP 보안 헤더(Security Headers) 적용 時 주의점

웹서버 Nginx를 이용해 HTTP 보안 헤더 적용 시 주의점에 대해서 알아봅니다. 그 동안 일반 인터넷에 널려 있는 방법으로 보안 헤더를 적용했는데 알고 보니 전혀 작동하지 않았더군요.

이는 최근 각광을 받고 있는 웹서버인 Nginx에서 볼 수 있는 주의점인데요. 한번 그 내용을 살펴보도록 하죠.

[쇼핑몰 보안] NGINX에서 HTTP 보안 헤더(Security Headers) 적용 時 주의점

쇼핑몰 구축기를 연재하는 이유

최근 지인이 워드프레스를 이용해 쇼핑몰 구축을 시도하면서 배웠던 배웠던 다양한 경험들을 해당 쇼핑몰 블로그에 연재해 왔는데요.

쇼핑몰이 상품만 파는 것이 아니라 쇼핑몰을 방문하는 고객들에게 열가지 유용한 정보를 제공하는 블로그의 효용성이 높다는 점을 십분 활용하고, 처음 시작하는 쇼핑몰의 신뢰성을 주기 위해 비록 삽질이지만 삽질기를 낱낱히 공개하기로 했다고 하네요.

그 쇼핑몰의 주소는  https://puripia.com로 아직도 공사중이기는 합니다.) 

쇼핑몰 구축 시 도와주었던 인연으로 그 쇼핑몰을 알리고 쇼핑몰 구축 경험담을 보다 널리 알리기 위해서 여기 happist.com에도 같이  공유합니다. 조금 사심이 있기는 합니다.

[쇼핑몰 보안] NGINX에서 HTTP 보안 헤더(Security Headers) 적용 時 주의점

쇼핑몰 보안을 살피다보면 참으로 여러가지를고민해야 한다는 것을 알 수 있습니다.

이전에 SSL인증서 설정을 가장 효과적으로 할 수 있는 방안에 대해 “SSL 보안 등급 A+에 도전하는 Let’s Encrypt 인증서 세팅 방법”라는 글에서 살펴 본적이 있는데요.

위 글에서는 SSL 인증서 관련 내용뿐만이 아니라 외에도 쇼핑몰이나 인터넷 문서의 헤더 부분의 보안을 강화하기 위한 HTTP Headers 보안 방안에 대해서 소개했었죠.

그런데 이 HTTP Headers 보안 옵션이 제대로 적용되려면 피해야할 주의점이 있습니다.

1. 적용했던 HTTP Sucrity Headers 옵션

우선 그동안 제가 적용했던 그리고 이번 쇼핑몰을 구상하면서 정리했던 최종 HTTP Headers 보안 옵션입니다.

각 옵션에 대한 설명은 인터넷에 아주 자세하게 널려있고, “SSL 보안 등급 A+에 도전하는 Let’s Encrypt 인증서 세팅 방법”라는 글에서도 어느 정도 설명해 놓았기 때문에 넘어가겠습니다.

# Security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options SAMEORIGIN always;
add_header Content-Security-Policy "default-src 'self' www.google-analytics.com ajax.googleapis.com www.google.com google.com gstatic.com www.gstatic.com connect.facebook.net facebook.com;";
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "origin";
# HTTP Public Key Pinning Extension HPKP for NGINX, https://raymii.org/s/articles/HTTP_Public_Key_Pinning_Extension_HPKP.html
add_header Public-Key-Pins 'pin-sha256="klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY="; pin-sha256="633lt352PKRXbOwf4xSEa1M517scpD3l5f79xMD9r9Q="; max-age=2592000; includeSubDomains';  
add_header Feature-Policy "geolocation none;midi none;notifications none;push none;sync-xhr none;microphone none;camera none;magnetometer none;gyroscope none;speaker self;vibrate none;fullscreen self;payment none;";Code language: PHP (php)

2. 적용되지 않는 HTTP Sucrity Headers 옵션

적용하고 있는 웹서버인 nginx 문법에 맞추어 HTTP Header의 보안 옵션을 적용했지만 막상 테스트해보면 적용되지 않았다고 나옵니다.

뭐 이를 테스트하는 방법은 아래처럼 여러가지 방법이 있습니다.

아래는 Mozilla Observatory에서 측정했는데 아무것도 적용디지 않았다고 나오는 황당한 상황을 캡춰한 것입니다.

[쇼핑몰 구축기] 웹서버 NGINX에서 HTTP 보안 헤더(Security Headers) 적용 時 주의점 12

3. 왜 HTTP Sucrity Headers 옵션은 적용되지 않을까?

HTTP 보안 헤더 적용 가이드에서 이야기한대로 충실하게 관련 옵션을 적용했는데요. .

그럼에도 불구하고 위에서 공유한 이미지처럼 실제로는 적용이 안되고 있습니다.

모든 테스트 결과에서 공통으로 적용되지 않았다고 나오기 때문에 의심의 여지가 없었습니다.

이 사이트뿐만이 아니라 비슷하게 적용하 다른 사이트도 다 적용이 안되더군요.

오랬동안 왜 그렇까하고 의구심만 품고 제대로 해결하지 못했는데요.

이번 쇼핑몰을 개발하면서, 쇼핑몰은 보안이 어떤 사이트보다도 중요하기 때문에 제대로 적용하자고 상당히 많은 고민과 검색 등을 통해서 해결 방안을 찾았습니다.

그리고 그 원인을 찾을 수 있었습니다. 이에 대한 실마리는 “Be very careful with your add_header in Nginx! You might make your site insecure”라는 글에서 찾을 수 있었습니다.

그것은 nginx 설정하는 conf 파일의 여러군데에서 add_header라는 명령이 나오면 앞에서 설정한 모든 add_header옵션은 무효화된다는 것입니다.

저는 그동안 서버 설정 문서에서 다양한 곳에서 add_header명령을 사용했던 것입니다. 그리고 맨 마디만에 사용한 add_header옵션은 거의 의미가 없는 옵션이었기 때문에 메인 옵션이 적용되지 않았던 것입니다.

아래 add_header가 남발된 몇몇 명령을 보시죠.

# Add PHP handler
location ~ \.php$ {
try_files $uri =404; # comment out this line if php-fpm is hosted on a remote machine
include /etc/nginx/fastcgi.conf;
fastcgi_cache WORDPRESS;
add_header X-Cache $upstream_cache_status;

# Aggressive caching for static files that rarely/never change
location ~ \.(?:asf|asx|wax|wmv|wmx|avi|bmp|class|divx|doc|docx|eot|exe|gif|gz|gzip|ico|jpg|jpeg|jpe|mdb|mid|midi|mov|qt|mp3|m4a|mp4|m4v|mpeg|mpg|mpe|mpp|odb|odc|odf|odg|odp|ods|odt|ogg|ogv|otf|pdf|png|pot|pps|ppt|pptx|ra|ram|svg|svgz|swf|tar|t?gz|tif|tiff|ttf|wav|webm|wma|woff|wri|xla|xls|xlsx|xlt|xlw|zip)$ {
expires 31536000s;
#add_header Pragma public; # HTTP1.0에서 사용하던 명령으로 같이 사용하지 않음
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}

location ~ \.(?:css|js)$ {
expires 86400s; 
#add_header Pragma public; # HTTP1.0에서 사용하던 명령으로 같이 사용하지 않음
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}Code language: PHP (php)

4. 해결 방안

문제 원인을 알았기 때문에 HTTP 보안 헤더 옵션을 적용하고 그 후에 나오는 add_header 옵션은 전부 제거했습니다.

이래더니 제대로 작동합니다. 생각보다 간단한 문제였는데요. 그것을 모르고 좋다는 옵션을 무조건 가져다 사용하다보니 이런 역효과가 나왔다는 교훈을 얻었습니다.

만약 뒤에서 굳이 add_header 옵션을 사용해야 한다면 general-security-headers.conf와 같은 보안 헤더 적용 옵션을 정의한 세팅 파일을 만들고, 적용 시점에서 불러오는 방법읗 사용하라고 조언하고 있습니다.

그 코드는 아래처럼 구성됩니다.

먼저 /etc/nginx 폴더에 general-security-headers.conf 파일을 만듭니다.

# general-security-headers.conf 파일 만들기
nano /etc/nginx/general-security-headers.conf

# 아래처럼 보안 헤더 옵션 설정
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options SAMEORIGIN always;
add_header Content-Security-Policy "default-src 'self' www.google-analytics.com ajax.googleapis.com www.google.com google.com gstatic.com www.gstatic.com connect.facebook.net facebook.com;";
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "origin";
# HTTP Public Key Pinning Extension HPKP for NGINX, https://raymii.org/s/articles/HTTP_Public_Key_Pinning_Extension_HPKP.html
add_header Public-Key-Pins 'pin-sha256="klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY="; pin-sha256="633lt352PKRXbOwf4xSEa1M517scpD3l5f79xMD9r9Q="; max-age=2592000; includeSubDomains';  
add_header Feature-Policy "geolocation none;midi none;notifications none;push none;sync-xhr none;microphone none;camera none;magnetometer none;gyroscope none;speaker self;vibrate none;fullscreen self;payment none;";Code language: PHP (php)

다음에는 사이트 conf 파일을 편집해 아래와 같은 내용을 추가합니다.

# 미리 만들어 놓은 보안 헤더 설정 파일을 불러옵니다.
include /etc/nginx/general-security-headers.conf;

location / {
try_files    $uri /index.html;
# 이 Location에서 독자적인 add_header 옵션을 가져야기 때문에 다시 불러옵니다.
include /etc/nginx/snippets/general-security-headers.conf;
}Code language: PHP (php)

5. 적용 결과 Test

제대로 적용이 되었는지 테스트해보았는데요.

이는 보안 헤더 부분만 집중적으로 다루고 있는 curity Headers Sponsored by netsparker 에서 테스트해 보았습니다.

HTTP 보안 헤더(HTTP Security Header) 적용 여부 점검 결과 by Security Headers Sponsored by netsparker

[쇼핑몰 구축기] SSL/TLS 보안 등급 A+에 도전하는 Let’s Encrypt 인증서 세팅 방법

0

여기서부터는 쇼핑몰의 보안 관련한 내용을 이어서 살펴 보겠습니다. 그 첫번째로 SSL/TLS 인증서 설치하고 보안을 강화하기 위해 세팅 방법에 대해서 알아보도록 하겠습니다.

SSL 보안 등급 A+에 도전하는 Let’s Encrypt 인증서 세팅 방법

쇼핑몰 구축기를 연재하는 이유

최근 지인이 워드프레스를 이용해 쇼핑몰 구축을 시도하면서 배웠던 배웠던 다양한 경험들을 해당 쇼핑몰 블로그에 연재해 왔는데요.

쇼핑몰이 상품만 파는 것이 아니라 쇼핑몰을 방문하는 고객들에게 열가지 유용한 정보를 제공하는 블로그의 효용성이 높다는 점을 십분 활용하고, 처음 시작하는 쇼핑몰의 신뢰성을 주기 위해 비록 삽질이지만 삽질기를 낱낱히 공개하기로 했다고 하네요.

그 쇼핑몰의 주소는  https://puripia.com로 아직도 공사중이기는 합니다.) 

쇼핑몰 구축 시 도와주었던 인연으로 그 쇼핑몰을 알리고 쇼핑몰 구축 경험담을 보다 널리 알리기 위해서 여기 happist.com에도 같이  공유합니다. 조금 사심이 있기는 합니다.

SSL 보안 등급 A+에 도전하는 Let’s Encrypt 인증서 세팅 방법

무료 SSL/TLS 인증서로 인기를 끌고 있는 Let’s Encrypt 인증서를 기반으로 A+를 받는 방법에 대해서 알아봅니다.

우선 SSL 보안 수준은 ssllabs.com에서 체크해 볼 수 있습니다.

ssllabs.com, SSL Server Test

Mozilla Observatory

1. 이제 A에 불과한 과거 SSL 인증서 세팅법

최근 우연한 기회에 sslabs.com에서 SSL Server Test를 하게 되었습니다. 그리고 충격을 먹었죠.

그 계기는 “네이버페이 연동 시 ‘인입로그 확인간 호스트네임 미일치로 인한 호출 실패’ 문제”를 해결하는 과정 중의 하나였습니다.

2~3년전, Let’s Encrypt를 이용한 SSL/TLS 세팅은 SSL Server Test에서 A+를 받기 에 충분했던 것 같은데요.

그런데 같은 세팅으로 이번에 테스트해보니 A정도만 나오고 군데 군데 보안이 취약하다는 메세지도 발견할 수 있습니다. 시간이 많이 흐른 것일까요?

let's encrypt ssl test score 보안 인증 점수

사실 알고보면 이 정도 점수라면 예전에는 A+이 나왔습니다. 평가하는 4개 항목의 점수가 95점이 넘으면 A+이라고 정의했던 것입니다.

그런데 최근에 A+의 기준을 아주 엄격하게 4개 항목을 모두 100점을 맞아야 하는 것으로 강화했습니다. 그러니 예전엔 A+이었던 것이 이제는 A밖에 나오지 않는 것이죠.

그래도 우선 어떻게 개선해야하는지 살펴보죠. 우선 예전에 적용했던 세팅들을 살펴봅니다.

add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

ssl_certificate /etc/letsencrypt/live/MyDomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/MyDomain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/MyDomain.com/chain.pem; 

ssl_dhparam /etc/nginx/ssl/dhparams.pem;

add_header Strict-Transport-Security "max-age=31536000";

ssl_session_timeout 10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';Code language: PHP (php)

2. 어떻게 SSL 인증서 보안을 강화할 것인가?

인증서 보안을 강화하기 위해서 아래 글들을 참조했습니다. 이를 통해서 전반적으로 개선 방향을 살펴보았습니다.

Strong SSL Security on nginx

Getting a Perfect SSL Labs Score

우리가 목표로하는 A+에 도전하기 위해서는 점수가 어떻게 구성되는지를 알아야 합니다. 위에서 소개한 ssllabs.com에서는 아래와 같은 네가지 항목으로 나누어 평가하고 있습니다.

  • Certificate
  • Protocol Support
  • Key Exchange
  • Cipher Strength

2.1. Certificate – Let’s Encrypt 적용만으로 충분

SSL 인증해주는 툴들은 굉장히 많이 있습니다. 그러나 우리는 무료로 적용 가능한 Let’s Encrypt를 사용하기로 했죠.

이 Let’s Encrypt는 시작한지 얼마되지 않았지만 기존 유료 SSL 인증서에 못지않은 뛰어난 성능을 가지고 있습니다.

Let’s Encrypt은 이 SSL인증 시장에서 점유율이 겨우 0.1%에 불과하지만 무려라는 장점을 기반으로 점점 그 영향력을 넓힐 것으로 보입니다.

다만 이 Let’s Encrypt는 3개월에 한번씩 인증을 갱신해야하는 단점이 있지만 자동 갱신 기능이 있으므로 어느정도 커버됩니다.

그리고 중요한 것은 Let’s Encrypt 적용 자체만으로도 Certificate부분은 만점을 받을 수 있습니다.

ssl_certificate /etc/letsencrypt/live/MyDomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/MyDomain.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/MyDomain.com/chain.pem; Code language: PHP (php)

2.2. Protocol Support – 지원 protocol을 제한한다.

다음으로 SSL 인증서의 보안을 강화하기 위해서는 지원 protocol이 적절한지를 보는 것입니다.

대부분 이미 SSLv2나 SSLv3를 지원하지 않으며 오직 TLS만 지원하는 경우가 일반적입니다.

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;Code language: PHP (php)

그러나 보안을 더 고려한다면 TLSv1.0과 TLSv1.1 지원을 중단하는 것이 좋다고 합니다. 이미 TLSv1.0은 시장에서 지원이 중단된 상태이기도 합니다.

그냥 점수로만 따지만 TLSv1 지원 중단시 95점(TLSv1 TLSv1.1 TLSv1.2 지원)에서 97점으로 높아지고 여기에 TLSv1.1까지 지원 중단한다면 100점이라는 평가를 합니다.

결국 T100점으로 완벽을 기하기 위해서는 LSv1.2와 TLSv1.3만 지원하는 것으로 정리합니다.

ssl_protocols TLSv1.3 TLSv1.2; # Requires nginx >= 1.13.0 else use TLSv1.2Code language: PHP (php)

2.3. Key Exchange – 보안성이 높은 보안 키 생성 및 적용

일반적으로 SSL/TLS만 적용해도 되지만 보안을 더욱 강화하기 위해서 보안키 수준을 높입니다.

근래에 등장한 웹서버로 인기를 끌고 있는 nginx는 openssl에서 제공하는 기본 DHE (Ephemeral Diffie-Hellman) 파라메터를 사용합니다.

이는 1024비트에 기반하기 때문에 낮은 점수를 받을 수 밖에 없습니다. 그래서 nginx 자체가 아닌 사용자가 더 높은 보안을 담보하는 키를 생성해 줍니다.

보통 이럴경우 예전에는 2048비트를 적용하지만 더 완벽한 보안을 추구하는 경우 4096비트 보안키를 적용합니다.

조금 오래되었지만 2048비트에 비해서 4096비트는 시간이 7배정도 더 걸린다는 지적도 있었고 2048비트면 보안으로 충분하다는 지적도 있었던 적이 있습니다.

그러나 최근에는 2048비트로는 보안에 취약해진다는 평가가 많아지는 것 같구요. 그래서 4096비트 적용을 많이 추천하는 것으로 보입니다.

이를 위한 명령은 아래와 같습니다. 4096비트다보니 생성 시간이 많이 걸립니다.

참고로 ssl 폴더를 nginx 아래에 만들기도하지만 다른 곳에 만들어 conf 파일에 정확한 폴더 위치만 지정해주면 됩니다.

mkdir /etc/nginx/ssl  
openssl dhparam -out /etc/nginx/ssl/dhparam.pem 4096Code language: PHP (php)

이후 nginx conf 파일에 아와 같은 명령을 추가합니다.

ssl_dhparam /etc/nginx/ssl/dhparam.pem;Code language: PHP (php)

2.4. Cipher 강화

보안강화와 호환성과의 아슬아슬한 줄타기가 필요한데요.

서버에서 직접 확인해보는 방법

서버에서 어떠한 Cipher을 사용해야하는지를 아래 명령으로 알아 볼 수 있습니다.

/usr/bin/openssl ciphers -s -vCode language: PHP (php)

그러면 아래 그림처럼 cipher 리스트가 주룩 뜹니다. 여기를 기반으로 cipher 리스트를 정리해도 됩니다. 다만 이 방법이 효욜적이고 정답을 찾을 수 있는 길인지는 모르겠습니다. 전문가의 영역이라는 생각이 들기 때문에..

nginx 서버에서 Cipher 리스트 찾는 명령 수행 후

모질라 재단 추천 이용 방법

이렇게 어렵게 하지말고 모질라 재단(theMozilla Foundation)에서 추천하는 명령을 이용하는 방법이 있습니다. .

여기에서는 nginx, Apache 등의 웹서버 버젼과 OpenSSL버젼 그리고 모던 브라우져 중심인지 아니면 구 부라우져까지 지원할 것인지 옵션에 따라 최적의 세팅 방안을 제안하고 있습니다.

웹서버등드의 버젼이 올라가면 세팅 방업이 변경되므로 종종 들러서 참고하는 게 좋을 것 같네요.

아래는 모던 브라우저 지원용 명령입니다. 테스트해보니 대부분의 운영체제에서 아주 완벽하게 호환됩니다.

다만 일부 보안이 취약하다는 경고가 나오고 덕분에 점수는 낮아져 목표한 100점 달성에는 실패합니다.

아래는 nginx 1.14, openssl 1.1.1e에서 모던 브라우저 지원 기준입니다.

ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';Code language: PHP (php)
모질라 재단에서 제안하는 옵션 적용 결과 세부 항목02

만약 IE6/WinXP를 지원한다면 아래 명령이 추천됩니다. 이는 훨씬 더 호환성을 높인 것이죠. 아래는 물로 모질라 재단 추천 명령을 인용했습니다.

마찬가지로 이 명령 또한 nginx 1.14, openssl 1.1.1e 기준입니다.

ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
    ssl_prefer_server_ciphers on;Code language: PHP (php)

2.5. SSL Stapling

SSL Stapling이 더 보안을 강화할 수 있느냐에 대해서는 설왕설래가 있지만 적용해 봅니다.

ssl_stapling on;
ssl_stapling_verify on;Code language: PHP (php)

2.6. SSL Sessions

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;Code language: PHP (php)

2.7. ssl_ecdh_curve

ssl_ecdh_curve secp384r1;Code language: PHP (php)

2.8. HTTP Headers

HTTP header에 최소한 추가가 좋다고 합니다.

add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options: nosniff;
add_header "X-XSS-Protection" "1; mode=block";
add_header Strict-Transport-Security: max-age=15768000; includeSubDomainsCode language: PHP (php)

X-Frame-Options

위 명령 중 첫번째에 나오는 X-Frame-Options은 페이지 안에 프레임과 같은 다른 페이지를 불러오는 것을 제어하는 것으로 아래처럼 세가지 옵션이 있는데요.

X-Frame-Options: deny
X-Frame-Options: sameorigin
X-Frame-Options: allow-from https://example.com/Code language: PHP (php)
  • deny는 모든 프레임을 무효하하는 것입니다.
  • sameorigin은 도메인 기준으로 같은 사이트 내에서는 불러올 수 이있지만 다른 사이트 페이지는 불러올 수 없습니다.
  • allow-from https://example.com/은 불러올 수 있는 주소를 설정하는 것입니다.

nginx에서는 아래와 같은 방식으로 적용합니다.

add_header X-Frame-Options sameorigin always;Code language: PHP (php)

X-Content-Type-Options

잘못된 MIME타입이 포함된 응답이 있으면 거부하라는 옵션입니다.

add_header X-Content-Type-Options: nosniff;Code language: PHP (php)

X-XSS-Protection

세번째의 add_header X-XSS-Protection 옵션은 XSS를 통한 세션 하이제킹을 막도록하는 명령으로 아래 네가지 옵션이 있습니다.

X-XSS-Protection: 0
X-XSS-Protection: 1
X-XSS-Protection: 1; mode=block
X-XSS-Protection: 1; report=<reporting-uri>Code language: PHP (php)

nginx에서는 아래와 같은 방식으로 적용합니다.

add_header "X-XSS-Protection" "1; mode=block";Code language: PHP (php)

Strict-Transport-Security

리스폰스 헤더가 HHTP대신 HTTPS만 통과토록 하라는 것으로 아래와 같은 옵션이 있습니다.

Strict-Transport-Security: max-age=<expire-time>
Strict-Transport-Security: max-age=<expire-time>; includeSubDomains
Strict-Transport-Security: max-age=<expire-time>; preloadCode language: PHP (php)

2.9. Gzip사용 중지

사이트 속도를 위해서 Gzip 사용이 적극 궈장되고 있지만 SSL 보안을 위해서는 Gzip 사용을 중지하라고 권고합니다.

gzip off;Code language: PHP (php)

3. SSL 인증서 세팅 최종 제안

위와 같은 여러 항목을 검토해 최적 설정으로 제안된 코드입니다.

nginx 사용 기준이구요. 모던 브라우저 지원 기준으로 구형 브라우져에 대한 지원은 채택하지 않았습니다.

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
 
ssl_protocols TLSv1.2 TLSv1.3; # Requires nginx >= 1.13.0 else use TLSv1.2
 
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;
 
ssl_stapling on;
ssl_stapling_verify on;
 
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
ssl_ecdh_curve secp384r1;

add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options: nosniff;
add_header "X-XSS-Protection" "1; mode=block";
add_header Strict-Transport-Security: max-age=15768000;Code language: PHP (php)

만약 쇼핑몰 중에서 다양한 구형 브라우저도 지원하겠다면 아래 내용으로 변경하면 좋을 것 같습니다.

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';Code language: PHP (php)

4. 마치며

이렇게 설정을 했음에도 불구하고 4가지 항목에 대해서 100점을 맞아 A+점수를 획득하기는 어렸웠습니다.

모던 브라우저 내에서의 호환성 유지를 고수한다면 key exchange나 Cipher Strength 측면에서 97점이상을 확보하기가 쉽지는 않더군요.

4가지 할목 모두에서 100점을 맞아 A+ 도전은 좀 더 연구해야할 것 같습니다.

[쇼핑몰 구축기] 우커머스 청구 및 배송 필드 최적화 방법

0

다시 우커머스 결제 단계로 돌아와 결제 단계 최적화 방법 중의 하나로 청구 및 배송 정보를 최소로 입력해 사용성을 높이는 방법을 살펴보죠.

계속 이야기하듯이 워드프레스 쇼핑몰을 구현해주는 우커머스는 미국적인 특성이 강하게 반영되어 있고 더우기 글로벌 비지니스를 염두에 두고 설계되었습니다.

따라서 미국식으로 성과 이름을 분리해 입려하게 되어 있으며, 주소도 국가 – 주 정보 등등 훨씬 더 많은 정보를 입력하도록 되어 있습니다.

그래서 한국에서만 쇼핑몰을 운영한다는 가정하헤 청구 및 배송 정보를 최소화하는 방법을 고민하는 것이죠.

[결제 단계 최적화] 우커머스 청구 및 배송 필드 변경법

쇼핑몰 구축기를 연재하는 이유

최근 지인이 워드프레스를 이용해 쇼핑몰 구축을 시도하면서 배웠던 배웠던 다양한 경험들을 해당 쇼핑몰 블로그에 연재해 왔는데요.

쇼핑몰이 상품만 파는 것이 아니라 쇼핑몰을 방문하는 고객들에게 열가지 유용한 정보를 제공하는 블로그의 효용성이 높다는 점을 십분 활용하고, 처음 시작하는 쇼핑몰의 신뢰성을 주기 위해 비록 삽질이지만 삽질기를 낱낱히 공개하기로 했다고 하네요.

그 쇼핑몰의 주소는  https://puripia.com로 아직도 공사중이기는 합니다.) 

쇼핑몰 구축 시 도와주었던 인연으로 그 쇼핑몰을 알리고 쇼핑몰 구축 경험담을 보다 널리 알리기 위해서 여기 happist.com에도 같이  공유합니다. 조금 사심이 있기는 합니다.

[결제 단계 최적화] 우커머스 청구 및 배송 필드 변경법

여기에서는 우커머스 결제 단계(checkout)에서 청구 주소 및 배송 주소지를 최적화하는 방법에 대해서 알아봅니다.

우커머스가 미국적인 특성이 강하기 때문에 한국 특성에 맞추고, 쇼핑 경험을 최적화하기 위해서는 청구 및 배송 필드를 최적화하는 것이 필요합니다.

1. 기본 우커머스 청구 상세 필드

계속 이야기되지만 우커머스는 미국 환경에 최적화되었기 때문에 그 특성을 반영해 다소 복잡하게 많은 정보를 입력하도록 되어 있습니다.

  • 미국 특성에 맞추어 이름과 성을 구분해 입력 받습니다.
  • 비지니스 특성을 반영해 회사명이 기본 입력토록 되어 있습니다.
  • 다국적 비지니스 플랫폼을 반영해 국가명 입력합니다.
  • 수십개의 주가 존재하기 때문에 주이름을 입력하는 필드가 따로 있습니다.
우커머스 결제(checkout) 단계 중 청구 상세 내용 화면

2. 청구 및 배송 필드 최적화

먼저 청구 및 배송 필드를 조정해 불필요한 필드를 제거합니다. 필드 제거에는 unset 명령을 사용합니다.

add_filter( 'woocommerce_checkout_fields', 'puripia_remove_fields', 9999 );
 
function puripia_remove_fields( $fields ) {
 
  // unset( $fields['billing']['billing_first_name'] );
  unset( $fields['billing']['billing_last_name'] ); // 성 필드 제거
  // unset( $fields['billing']['billing_phone'] );
  // unset( $fields['billing']['billing_email'] );
  unset( $fields['billing']['billing_company'] ); // 회사 필드 제거
  unset( $fields['billing']['billing_country'] ); // 국가 필드 제거
  //unset( $fields['billing']['billing_address_1'] );
  //unset( $fields['billing']['billing_address_2'] );
  unset( $fields['billing']['billing_city'] ); // 도시 주소 필드 제거
  unset( $fields['billing']['billing_state'] ); // 주(state) 줏 필드 제거
  //unset( $fields['billing']['billing_postcode'] ); 
 
  // unset( $fields['shipping']['shipping_first_name'] );
  unset( $fields['shipping']['shipping_last_name'] ); // 성 필드 제거
  // unset( $fields['shipping']['shipping_email'] );
  // and to remove the shipping fields below
  unset( $fields['shipping']['shipping_company'] ); // 회사 필드 제거
  unset( $fields['shipping']['shipping_country'] ); // 국가 필드 제거
  //unset( $fields['shipping']['shipping_address_1'] );
  //unset( $fields['shipping']['shipping_address_2'] );
  unset( $fields['shipping']['shipping_city'] ); // 도시 주소 필드 제거
  unset( $fields['shipping']['shipping_state'] ); // 주(state) 줏 필드 제거
  //unset( $fields['shipping']['shipping_postcode'] ); 

  // unset( $fields['order']['order_comments'] ); 

  return $fields;
}Code language: PHP (php)

3. 배송처 전화번호 필드 추가

우커머스는 배송처 전화번호에 대한 필드가 없습니다. 그러나 일반적으로 배송 시 물건 수령하실 고객과 연락하려면 전화번호가 필수적입니다.

아래와 같은 코드를 사용해서 배송처 전화번호 필드를 추가합니다.

add_filter( 'woocommerce_checkout_fields' , 'puripia_shippping_phone', 9999 );

function puripia_shippping_phone( $fields ) {
     $fields['shipping']['shipping_phone'] = array(
        'label'     => __('배송지 전화번호', 'woocommerce'),
        'placeholder'   => _x('받는 분 전화번호', 'placeholder', 'woocommerce'),
        'required'  => true,
        'class'     => array('form-row-wide'),
        'clear'     => true
     );

     return $fields;
}
Code language: PHP (php)

4. 필드마다 플레이스 폴더 지정

플레이스 홀더(place holder)란 입력 박스를 간략하게 설명하거나 가이드를 주는 문구입니다.

이 플레이스 홀더(place holder)는 눈에는 보이지만 여기에 문구를 입력하면 사라집니다. 제대로 입력받기 위한 가이드로 활용합니다.

위에서 새롭게 만든 배송처 전화번호는 이미 플레이스 홀더(place holder)를 비롯한 모든 정보가 입력되었고, address_1과 address_2도 적정한 가이드가 입력되어 있으니 넘어가기로 합니다.

아래와 같은 코드를 사용했습니다.

add_filter( 'woocommerce_checkout_fields' , 'puripia_labels_placeholders', 9999 );
 
function puripia_labels_placeholders($fields) {
 
  //$fields['billing']['billing_first_name']['label'] = '이름';

    $fields['billing']['billing_first_name']['placeholder'] = '고객 이름';
    $fields['billing']['billing_email']['placeholder'] = '고객 이메일 주소'; 
    $fields['billing']['billing_phone']['placeholder'] = '고객 전화번호';  
    $fields['billing']['billing_postcode']['placeholder'] = '우편번호-주소검색 시 자동입력';
    $fields['billing']['billing_address_1']['placeholder'] = '도로명/번지-주소검색 시 자동입력';
    $fields['billing']['billing_address_2']['placeholder'] = '아파트 동호수 등';

    $fields['shipping']['shipping_first_name']['placeholder'] = '받는 분 이름';
    $fields['shipping']['shipping_phone']['placeholder'] = '받는 분 전화번호';    
    $fields['shipping']['shipping_postcode']['placeholder'] = '우편번호-주소검색 시 자동입력';
    $fields['shipping']['shipping_address_1']['placeholder'] = '도로명/번지-주소검색 시 자동입력';
    $fields['shipping']['shipping_address_2']['placeholder'] = '아파트 동호수 등'; 

  return $fields;
 
}Code language: PHP (php)

5. 청구 및 배송 필드 순서 지정

청구 내용 및 배송지 내용을 입력하기 쉽도록 필드 순서를 지정합니다. 이는 사용자 경험등을 고려해 적절하게 조정합니다.

아래와 같은 코드를 이용해서 필드 순서를 조정합니다.

add_filter("woocommerce_checkout_fields", "reorder_woocommerce_fields", 9999);
function reorder_woocommerce_fields($fields) {
    $fields['billing']['billing_first_name']['priority'] = 10;
    $fields['billing']['billing_email']['priority'] = 20; 
    $fields['billing']['billing_phone']['priority'] = 30;  
    $fields['billing']['billing_postcode']['priority'] = 40;
    $fields['billing']['billing_address_1']['priority'] = 50;
    $fields['billing']['billing_address_2']['priority'] = 60;

    $fields['shipping']['shipping_first_name']['priority'] = 10;
    $fields['shipping']['shipping_phone']['priority'] = 30;    
    $fields['shipping']['shipping_postcode']['priority'] = 40;
    $fields['shipping']['shipping_address_1']['priority'] = 50;
    $fields['shipping']['shipping_address_2']['priority'] = 60;

    return $fields;
}
Code language: PHP (php)
우커머스 결제(checkout) 단계 중 청구 및 배송 필드 플레이스 홀드 내용