12.7 C
New York
금요일, 12월 19, 2025

Buy now

[광고] 쿠팡 추천 링크

안녕하세요? 올해까지 삼성전자 25년 직장 생황릃 마치고 퇴직하려 합니다. 퇴직 후 아르바이트로 쿠팡 파트너스 활동을 하려고 합니다. 쿠팡 파트너스는 쿠팡 추천 링크를...

카누 캡슐 커피머신 솔직 리뷰: ‘네스프레소 호환’ 가성비 끝판왕 (ft. 쿠팡 최저가 할인)

'공유 커피' 카누가 만든 카누 캡슐 커피머신 : 캡슐 커피머신 바리스타 브리즈/어반! 네스프레소 오리지널 캡슐 호환으로 활용도는 높이고, 카누만의 황금 레시피로 커피 맛은 깊어졌습니다....
Home Blog Page 275

[쇼핑몰 구축기] 보안을 위한 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 기준 1

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

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

SSL Security Test

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

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까지 세마리 토끼를 잡자 3

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

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

3. HSTS 등록하기

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

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

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

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

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

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

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

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

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) 적용 時 주의점 6

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) 단계 중 청구 및 배송 필드 플레이스 홀드 내용

[쇼핑몰 구축기] 우커머스 배송비 표시 – 무료時 기본 배송비 숨기기

0

배송 관련 정보도 미국 온라인 쇼핑과 한국 온라인 쇼핑간 특징이 극명하게 들어나는 요인중의 하나입니니다.

미국은 배송 지역이 넓기 때문에 상대적으로 다양한 배송 옵션을 제공합니다.

  • 비용이 달라지는 배송 기간/배송 방법,
  • 상품을 받는 위치를 집이 아닌 특정 유통 매장으로 설정 등 등

이처럼 다양한 방법을 제공하기 때문에 결제화면에 다양한 배송 방법을 제공하고 소비자가 선택하도록 하고 있습니다.

이러한 연유로 우커머스에서는 우리나라에서 흔히 제공하는 무료 배송 옵션과 배송비 추가 옵션이 같이 나타나 불필요한 오해와 실수가 발생할 수 있죠.

여기에서는 무료 배송의 경우 무료만 표시되고 나머지는 표시되지 않도록하는 방법에 대해서 살펴봅니다.

우커머스 배송비 표시 – 무료時 기본 배송비 숨기기

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

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

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

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

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

우커머스 배송비 표시 – 무료時 기본 배송비 숨기기

[결제 단계 최적화] 우커머스 배송비 표시 – 무료 배송 時 기본 배송비 숨기기

이번에는 배송비와 관련해 사용 경험을 최적화하기 위해 무료 배송비인 경우에는 기본 배송비를 표시하지 않는 방법에 대해서 알아봅니다.

이 글은 이전에 설명했던 “우커머스 배송 지역 및 배송비 설정 방법“과 함께 읽어보면 좋을 듯 합니다.

우커머스는 아무래도 미국 등 서구 지역 중심으로 발전한 프로그램이므로 미국적 쇼핑몰의 특성을 많이 반영하고 있습니다.

미국에서는 배송 기간, 배송 방법 그리고 배송 비용에 따라 다양한 옵션을 제공하고 있습니다.

예를들어 노트북을 구매한다고 할 시 이 노트북을 집으로 지접 배달받을 수도 있고, 아니면 가까운 베스트바이 매장에서 찾아갈 수도 있죠. 그리고 집으로 배송하는 경우도 당일 배송이냐 2일내 배송니냐 5일 내 배송 등 다양한 옵션이 존재합니다.

그렇기대문에 결제화면에서도 이러한 옵션을 소비자가 선택할 수 있도록 옵션을제공합니다.

다양한 배송비 표시 시 문제

우니나라도 최근 이렇게 다양한 옵션을 제공하는 방향으로 점점 변하고 있기는 합니다. 그렇지만아직도 여러 조건들은 매우 단순합니다. 그렇기에 굳이 결제화면에서 배송에 대한 복잡한 옵션을제공할 필요가 낮습니다.

특히나 배송비를 구매 금액과 쿠폰등으로 무료 옵션을 제공하는 경우 옵션을 모두 보여주는 것은 잘못하면 문제의 소지조차 있기도 합니다. 그래서 단순하게 최종 확정된 옵션만 보여주기를 선호합니다.

아래처럼 배송비에 무료와 기본이 모두 표현되고 있다면, 비록 체크 표시로 구분된다고는 하지만 혼란의 여지가 있기도하고, 고객이 실수로 다른 곳을 체크해 배송비 부여가 잘못 될 수 있습니다.

장바구니 모습, 무료와 기본 배송비가 모두 표현되고 있는 상황

고객이 버튼을 옮겼다고 하드라도 말썽의 소지가 다분하므로 차라리 확정된 하나만 나타나도록 하는 것이 필요하죠.

무료일 때 배송비 표시 시 기본 배송료 숨기기

이렇게 두가지가 모두 보이는 경우는 구매액이나 쿠폰으로 인해 무료 배송 조건이 되었을 때입니다.

따라서 무료 배송으로 전환되었을 시 고정 배송비 또는 기본 배송비는 보이지 않도록 만드는 것이 좋습니다.

이를 해결해주는 코드입니다. 이 코드를 차일드 테마 function.php에 추가합니다.

add_filter( 'woocommerce_package_rates', 'hide_fixed_shipping_cost_when_free_is_available', 100 );

function hide_fixed_shipping_cost_when_free_is_available( $rates ) {
  $free = array();
  foreach ( $rates as $rate_id => $rate ) {
    if ( 'free_shipping' === $rate->method_id ) {
      $free[ $rate_id ] = $rate;
      break;
    }
  }
  return ! empty( $free ) ? $free : $rates;
}Code language: PHP (php)

또는 아래와 같은 코드를 적용합니다. 이 코드는 무료 배송의 경우 모든 지역에서 모든 배송 방법을 표현하지 않는다고 합니다.

Snippet #2: Unset ALL rates in ALL zones when ANY Free Shipping rate is available

add_filter( 'woocommerce_package_rates', 'puripia_unset_shipping_when_free_is_available_all_zones', 10, 2 );
  
function puripia_unset_shipping_when_free_is_available_all_zones( $rates, $package ) {
     
    $all_free_rates = array();
    
        foreach ( $rates as $rate_id => $rate ) {
      if ( 'free_shipping' === $rate->method_id ) {
         $all_free_rates[ $rate_id ] = $rate;
         break;
      }
   }
    
   if ( empty( $all_free_rates )) {
        return $rates;
        } else {
        return $all_free_rates;
        } 
}Code language: PHP (php)

위 코드를 적용하면 위와 같은 조건에서 무료 하나만 보입니다. 아래 이미지를 참조하시죠.

이미지에 대체텍스트 속성이 없습니다; 파일명은 장바구니-모습-무료만-표현된-상황-1024x649.png 입니다.

[쇼핑몰 구축기] 우커머스 배송 지역 및 배송비 설정 방법

0

우커머스에서 배송 지역 및 배송비 설정 방법에 대한 다양한 글들이 많지만 최근 우컴스 버젼을 기반으로 정리한 글을 찾기 어려웠습니다.

최근 우커머스 버젼으로 우커머승서 배송 지역 및 배송비 성정 방법에 대해 간단히 살펴봅니다.

우커머스 배송 지역 및 배송비 설정 방법

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

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

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

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

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

우커머스 배송 지역 및 배송비 설정 방법

여기에서는 실제 상품이 판매 시 배송비 등을 어떻게 책정할 것인지에 대해서 알아 봅니다.

인터넷에는 우커머스에서 배송 지역 및 배송비 설정 방법에 대한 다양한 글들이 많지만 최근 우컴스 버젼을 기반으로 정리한 글을 찾기 어려웠습니다.

그래서 크게 다르지는 않지만 최신 버젼을 기반으로 우커머스 배송 지역 설정 및 배송비 설정 방법에 대해 정리해 봤습니다.

1. 일반 배송 설정

배송에 대한 일반 옵션 내용을 정하는 곳입니다.

판매지역을 어디까지 할 것인지, 이에 따라 배송 지역은 어디까지 할것인지 등등 설정합니다.

이는 우커머스 – 설정 기본에서 할 수 있습니다.

  1. 판매 지역 : 모든 국가에 판매할 것인지? 아니면 특정 국가에서만 판매할 것인디 등의 판매 지역을 정합니다.
  2. 특정 국가 : 만약 특정국가만 판매한다고 했으며 그 특정국가가 어디인지를 정합니다. 여기에서는 대한민국만을 정의했습니다.
  3. 배송 지역 : 판매하는 모든 국가에 배송할 것인지 아니면 특정 국가만 배송할 것인지를 정의합니다. 이는 아마존 어필리에이트처럼 전세계를 대상으로 판매는 하되 배송은 담당하지 않는 등 다양한 상황에 맞추어 세팅할 수 있습니다.
우커머스 배송 기본 설정 화면

2. 배송설정 – 배송관련 기본 사항

우커머스 배송설정에서는 배송비 계산을 어떻게 할것인지, 배송 목적지를 어디로 설정할 것인지, 디버그 모드를 활성화할 것인지를 설정합니다.

  1. 장바구니 페이지에 배송 계산기 활성화 : 장바구니 페이지에서 배송비를 계산하고, 알수 있도록 하는 옵션입니다.
    우리나라 고객들은 최종 결제 단계에서 갑자기 금액이 올라가는 것을 굉장히 싫어하므로 가능하면 장바구니에서 배송비를 포함한 비용을 알 수 있도록 하는 것에 좋다고 합니다.
  2. 주소가 입력될 때까지 배송 비용 감추기 : 마찬가지로 갑자기 배송료가 튀어나오는 것이 쇼핑 경험에 그리 좋지는 않습니다.
  3. 배송 목적지 : 고객 청구 주소를 기본으로하고 필요시 배송 주소를 추가 입력할 수 있게 합니다.
  4. 디버그 모드 : 배송 디버그 모드를 활성화하면 결제 단계에서 배송 방식 메세지를 뿌려줍니다. 이는 불필한 정보에 가까우므로 사용하지 않습니다.
우커머스 배송설정 화면

3. 배송구역 설정

여기에서는 배송구역을 어떻게 나눌 것인지를 정의합니다.

대한민국을 하나의 배송구역을 보고 전국을 동일한 배송비 정책을 사용할 수도 있고, 대한민국을 몇개의 구역으로 나누어서 각기 다른 배송 설정을 할 수 있습니다.

우리가 그동안 인터넷 쇼핑 시 많이 보았던 도서산간지역, 제주도 등은 분리해서 추가 비용을 받는 것이 일반적이긴 합니다.

따라서 여기서는 대한민국 기본 배송 지역과 도서산간지역 그리고 제주도로 나누는 배송구역을 설정했습니다.

그리고 배송 구역의 순서는 아래 이미지처럼 제주도 > 도서산간지역 > 기본 배송 지역순으로 나열합니다. 그래야 지역적 특성이 제대로 반영됩니다.

우커머스 배송설정_배송구역 설정 화면

여기서 도서산간지역과 제주도를 어떻게 나누느냐가 관건인데요.

4. 배송구역 상세 설정

여기에서는 배송구역을 어떻게 나눌 것인지를 정의합니다.

대한민국을 하나의 배송구역을 보고 전국을 동일한 배송비 정책을 사용할 수도 있고, 대한민국을 몇개의 구역으로 나누어서 각기 다른 배송 설정을 할 수 있습니다.

우리가 그동안 인터넷 쇼핑 시 많이 보았던 도서산간지역, 제주도 등은 분리해서 추가 비용을 받는 것이 일반적이긴 합니다.

따라서 여기서는 대한민국 기본 배송 지역과 도서산간지역 그리고 제주도로 나누는 배송구역을 설정했습니다.

3.1. 도서 산간 지역 설정

배송구역 추가를 선택해, 구역 명을 적고 구역 포함 지역을 입력합니다. 도서 산간 지역의 구역 포함 지역은 G마켓에서 설정한 도서 산간 지역에 해당하는 우편번호를 입력하면 됩니다.

아래는 도서 산간 지역에 해당하는 우편번호입니다.

22386...22388
23004...23010
23100...23116
23124...23136
31700
31708
32133
33411
40200...40240
52570...52571
53031...53033
53089...53104
54000
56347...56349
57068...57069
58760...58762
58800...58806
58809...58810
58816...58818
58826
58828...58866
58953...58958
59102...59103
59106
59127
59129
59137...59145
59149...59166
59421
59531
59551
59563
59568
59650
59766
59781...59790Code language: PHP (php)

아래는 우커머스 – 배송 – 배송구역에서 도서산간지역을 설정한 모습니다.

우커머스 배송 구역 추가_도서산간지역 설정

3.2. 제주도 구역 설정

앞의 도서 산간 지역과 마찬가지로 제주도 지역도 제주도에 해당하는 우편번호를 사용해 제주도 구역 포함 지역을 설정합니다.

아래는 제주도 지역에 해당하는 우편버호입니다.

6300063644Code language: PHP (php)
[쇼핑몰 구축기] 우커머스 배송 지역 및 배송비 설정 방법 7

5. 배송 비용 설정

배송구역이 설정되었으며 배송 비용을 설정합니다.

이 배송 비용은 각 배송구역에서 고정 요금을 받을 지, 아니면 무료로 할지 등등을 정의합니다.

이 사이트의 초기 설정은 배송 구역별 기본 요금을 설정하고 일정 조건이 되면 무료 배송토록 했습니다.

배송구역에서 배송 방법 추가 버튼을 누르면 고정 요금/무료배송/방문수령 중 하나를 선택해 세팅할 수 있습니다.

우커머스 배송 설정_배송 방법 추가

만약 고정요금을 선택하면 아래 이미지처럼 세금 상태와 배송 비용을 설정할 수 있습니다.

우커머스 배송 구역 추가_도서산간지역 배송비용

무료배송 설정은 조금 더 손이 가기는 합니다. 우선 무료 배송 조건을 선택하고 , 필요에 따라 최저 주문 금액을 입력합니다.

여기에서는 최소 주문 금액 또는 쿠폰을 선택하고 최저 주문 금액을 3만원으로 세팅했습니다. 아래는 그러한 설정 단계를 보여주고 있습니다.

[쇼핑몰 구축기] 우커머스 배송 지역 및 배송비 설정 방법 8
우커머스 배송 구역 추가_제주도 무료 배송 설정02

그러면 지역 내에서 고정요금과 무료배송의 순서를 어떻게 나영해야 할까요?

배송구역내에서 배송 비용 조건은 무료 배송을 먼저 위치시키고 이어 고정 배송 요금을 후순위로 위치시킵니다.

[쇼핑몰 구축기] 우커머스 배송 지역 및 배송비 설정 방법 9

[쇼핑몰 구축기] 원페이지 체크아웃(One Page Checkout) 구현으로 구매 단계 줄이기

0

이전에 바로구매 버튼을 통해서 쇼핑 단계를 줄이는 방법에 대해서 살펴 보았는데요. 이렇게 구매 단계를 줄이는 다양한 방법이 존재합니다.

오늘 소개하려는 원페이지 체크아웃(One Page Checkout)은 장바구니 확인과 결재 단계를 동시에 처리할 수 있는 방법입니다.

[결제 단계 최적화] 우커머스 원페이지 체크아웃(One Page Checkout) 구현 방법

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

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

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

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

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

[결제 단계 최적화] 우커머스 원페이지 체크아웃(One Page Checkout) 구현 방법

여기에서는 결제 단계 최적화 기법의 하나로 원페이지 체크아웃에 대해서 알아봅니다.

체크아웃 페이지를 구성하는 방식은 체크아웃 단계를 몇 페이지로 구현했느냐에 따라서 원페잊 체크아웃과 다단계 체크아웃(Multi-step checkout)으로 나눌 수 있습니다.

1. 원페이지 체크아웃(One Page Checkout)이란

원페이지 체크아웃이란 구매 상품 리뷰(구매 상품 갯수 및 구매 여부 조절), 청구 및 배송지 입력 그리고 결제 수단까지를 한 페이지에서 모두 진행하는 것을 말합니다.

여러 단계를 거치지 않고 한 페이지에서 진행되므로 상대적으로 빠르고 간편하게 결제를 진행할 수 있습니다.

  • 상대적으로 클릭해야하는 횟수가 줄어듭니다.
  • 여러 단계를 거치지 않기 때문에 보다 쉬워 보입니다.

반면에 부정적인 면도 분명히 존재합니다.

  • 원페이지 체크아웃은 상대적으로 길어지기 때문에 고객들이 부담을 느낄 수 있습니다. 부담을 느끼고 쇼핑을 중단할 가능성도 있죠.
  • 상대적으로 길기 때문에 고객들이 어디까지 했는지 모를 수 있고, 혼란스러워 할 수 있습니다. 이는 앞에서 지적 사항과 연관이 깊습니다.
  • 사이트 속도가 느려집니다. 한페이지에 많은 정보를 제공하려고 하기 때문에 속도가 느려질 수 있고 이는 고객들이 쇼핑을 중단할 수 있는 위험성을 가지고 있습니다.
  • 분석하기가 애매합니다. 원페이지 체크아웃의 어느 단계에서 쇼핑을 중단했는지와 같은 중요한 정보를 얻기 힘듭니다.

2. 원페이지 체크아웃 구현 방법

원페이지 체크아웃을 가장 잘 구현하려면 어떻게 해야할까요?

2.1. WooCommerce One Page Checkout(79$)

컨셉적으로 원페이지 체크아웃은 우커머스 원페이지 체크아웃(WooCommerce One Page Checkout) 플러그인이 잘 구현되어 있다는 생각입니다.
원페이지 체크아웃 컨셉에 충실한 플러그인이라고나 할까요?

여기에서는 Choose Your product, Billing Details, Additional Information, Your order, Proceed checkout을 한 페이지에서 구현해 놓고 있습니다.

아래 이미지를 참조해 보시죠.

[쇼핑몰 구축기] 원페이지 체크아웃(One Page Checkout) 구현으로 구매 단계 줄이기 10

2.2. Woocommerce one page checkout and layouts

얼마전에 배포되기 시작한 완전히 새로 등장한 플로그인이 Woocommerce one page checkout and layouts입니다.

이 플러그인은 한 페이지 내에서 Billing details, Shipping methods, Payment Method, Conform Your Order, Cart total, Proceed To Paypal과 같은 단계를 한페이지에서 보여주고 있습니다.

사용해보지는 않았는데 참고로 공유드립니다.
아래는 플러그인 소개 시 공유하는 원페이지 체크아웃 이미지입니다.

[쇼핑몰 구축기] 원페이지 체크아웃(One Page Checkout) 구현으로 구매 단계 줄이기 11

3.3. WooCommerce Direct Checkout Pro(10$)

최근에 WooCommerce express checkout이라는 이름으로 새롭게 런칭하고 있는 “우머커스 다이렉트 체크아웃 프로(WooCommerce Direct Checkout Pro) 플러그인은 “바로구매” 기능과 “원페이지 체크아웃”기능을 동시에 제공하고 있는 플러그인입니다.

제가 사용하는 플러그인으로 가격은 상대적으로 저렴하고, 싼 가격만큼 원페이지 체크아웃 기능은 섬세하지 못합니다. 특히 모바일 최적화가 아직 이루어지 않았습니다.

아래 초기 세팅 후 모바일에서 보여지는 모습압니다.

[쇼핑몰 구축기] 원페이지 체크아웃(One Page Checkout) 구현으로 구매 단계 줄이기 12

이 플러그인의 모바일 최적화에 대해서는 아래 글을 참조해 조정하면 좋을 것 같습니다.

[쇼핑경험 개선] 우커머스 쇼핑몰에 바로구매 버튼 추가로 구매 단계 줄이기

여러가지 튜닝을 거치면 모바일에서 이와 같은 모습으로 보여질 수 있습니다.

WooCommerce Direct Checkout Pro 원페이지 체크아웃 최적화 후 모습

[쇼핑몰 구축기] 바로구매 버튼 추가로 구매 단계 줄이기

0

우리나라 쇼핑몰엔 기본으로 달려있는 바로구매 버튼이 우커머스에는 기본이 아니죠. 그래서 커스터마이징을 통해서 바로구매 버튼을 달아야 합니다.

문화적인 차이 또는 아마존 특허의 이슈인지는 확실하지는 않지만 우커머스에는 관련 기능이 없습니다. 그리고 바로구매 버튼을 제공하는 예전 자료중에는 특허에 상관없이 달겠다는 체크 표시가 있었던 것을 보면 분명 측허 이슈가 있었던 것은 분명해 보입니다.

이제는 특허기간이 소멸되었지만 아마존이 원크릭 쇼핑과 같은 온라인 쇼핑 시 편리한 기능들에 대해 특허를 가지고 있었죠. 아마존의 원클릭쇼핑이나 아마존 대시에 대해서는 아래 글을 참조해 보세요

쇼핑몰을 대체하겠다던 아마존 대시(Amazon Dash)는 실패작일까?

여기에서는 우커머스 쇼핑몰에서 플러그인을 사용해서 바로구매 버튼 추가 방법에 대해서 알아봅니다.

[쇼핑경험 개선] 우커머스 쇼핑몰에 바로구매 버튼 추가로 구매 단계 줄이기

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

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

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

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

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

[쇼핑경험 개선] 우커머스 쇼핑몰에 바로구매 버튼 추가로 구매 단계 줄이기

우리나라의 대부분 쇼핑몰에서는 기본적으로 바로구매 버튼을 제공합니다.

기존에는 장바구니(Shopping Cart)에 담기 – 장바구니(Shopping Cart) – 결제 단계로 이어지는 다소 복잡한 프로세스를 거쳤지만 바로구매 버튼은 바로 구매(Buy Now) – 결제로 이어지도록 만들었기 때문에 그만큼 쇼핑 경험을 개선할 수 있습니다.

우리나라 쇼핑몰에서 매우 당연한 기능이지만 우커머스에서는 기본적으로 제공하지는 않는 기능으로 플러그인을 사용하든 프로그램으로 추가하든 별도 작업이 필요합니다.

이번에는 쇼핑 경험을 개선하기 위해서 워드프레스 쇼핑몰에서 바로구매 버튼을 추가하는 방법에 대해서 살펴보겠습니다.

1. 플러그인 사용

워드프레스 장점을 극대화하기 위해 별도의 프로그램하지 않고 관견 기능을 제공하는 플러그인을 구매하여 사용할 수 있습니다.

1.1 유료 플러그인

바로구매 기능 구현을 위한 가장 간편한 방법은 유료 플러그인을 구매하는 것입니다. 제가 알아본 바로 구매를 위한 유료 플로그인은 아래와 같습니다.

개인적으로 처음엔 어느정도 비용을 들이고자 가격이 상대적으로 저렴한 WooCommerce Direct Checkout Pro를 구입했습니다.

코드엠샵 바로구매 플러그인 29,900원

다보리 통합 플러그인

WooCommerce Direct Checkout Pro, 10$

1.2. 무료 플러그인

바로구매 기능 구현을 위한 무료 프로그램은 Quick Buy For WooCommerce가 있는데요.

이 플러그인은 아쉽게도 업데이트가 1년동안 거의 이루어지지 않고 있습니다. 그래서 사용하기가 매우 찜찜합니다.

오래전에 등장한 다른 플러그인도 있지만 그것들은 더 오래전부터 업데이트가 안되고 있습니다. 아쉬운 일이죠.

1.3. 프로그래밍 추가

플러그인들이 마음에 들지 않는다면 개인 취향을 최대한 반영한 프로그램을 통해서 바로구매를 반영할 수 있습니다.

이는 어느 정도 프로그램에 대한 능력이 요구되기 때문에 일반인들이 쉽게 접근하기는 쉽지는 않습니다.

2. 유료 플러그인 WooCommerce Direct Checkout Pro 사용기

앞서 잠깐 언급했듯이 바로구매 기능 구현을 위해서 WooCommerce Direct Checkout Pro를 구매했습니다.

이 플러그인은 아래와 같이 다양한 기능을 가지고 있습니다.

  • AJAX ADD TO CART – 이 기능은 장바구니에 담은 상품을 장바구니까지 가서 볼 필요없이 간단히 메뉴에서 장바구니 현황을 볼 수 있습니다.
  • 결제 단계의 불필요한 필드 제거 기능 – 예를 들어 동양에서는 성과 이름을 구분해서 잊력하지 않기 때문에 성에 해당하는 last_name, 국가, 주 등 불필요한 필드드릉ㄹ 제거할 수 있는 기능이 있습니다.
  • 프로 버젼에서는 바로구매 버튼 추가 기능이 있습니다. 일반 버젼에서는 구매버튼을 바구구매 버튼으로 변경 기능을 제공합니다. 구매와 바로구매 버튼 두개다 필요없다면 일반 버젼을 사용해도 좋을 듯 하네요.
  • 프로버젼에서는 ONE-PAGE CHECKOUT이라는 결제단계에서 상품 수량 및 상품 개별 리스트 제거 기능을 제공합니다.

다양한 기능을 10$에 제공하기 때문에 그리 나쁜 선택은 아니라는 생각입니다. 그러나 아직은 부족한 점이 몇개 보입니다.

아래에서는 이러한 문제를 수정해 가는 과정을 케이스가 발생하는대로 공유하도록 하겠습니다.

2.1. 장바구니 버튼과 바로구매 버튼 매칭 문제

이러한 플러그인의 경우 버튼간 매칭 문제가 항상 발생합니다.

이럴경우 css 프로그램을 통해서 매칭하라고 하는데요. 이는 일반인의 능력을 넘어서는 분야이기 때문에 보다 많은 공부가 필요합니다.

다향히 이 WooCommerce Direct Checkout Pro의 경우 대부분 브라우저에서 장바구니 버튼과 바로구매 버튼 매칭에는 큰 문제가 없었습니다.

다만 소소한 두가미 문제를 만났는데요. 참고로 그 문제와 해결 과정을 적어 보도록 하겠습니다.

2.1.1. 장바구니 버튼과 바로구매 버튼 배열이 깨지는 경우

어느날 평소에는 잘 사용하지 않던 엣지로 테스트 했는데 여기서는 문제가 발생하더군요

문제는 아래 이미지처럼 마이크로소프트 엣지에서는 장바구니 버튼과 바로구매 버튼 배열이 깨지는 현상이 발생한 것입니다. 이는 오직 엣지에서만 그랬습니다.

[쇼핑몰 구축기] 바로구매 버튼 추가로 구매 단계 줄이기 13

이럴 경우 어떻게 해야할까요? 무엇을 점검해 보아야 할까요?

이 문제 해결은 생각보다 쉽게 찾았습니다. 사용하는 OceanWP 테마 문제 해결을 위해 세팅을 변경하면서 버튼들이 들어갈 공간이 부족해진 원인이었습니다.

즉 이미지와상품 설명 공간 배치 비율을 성정하면서 이미지를 크게 보이겠다는 생각에 상대적으로 이미지 공간크기를 늘렸는데 엣지와 다른 브라우져간 마진이나 pedding을 받아드리는 값이 달랐던 것 같습니다.

주로 파이어폭스 기준으로 작업해 아무 문제없을 정도로 이미지 공간크기를 ㄴㄹ렸는데 엣지에서는 상품 설명 공간이 더 작아지고 버튼 2개가 나란히 들어갈 공간이 부족해진 것이죠.

결국 이미지 공간 비율을 줄이고 상품 설명 공간 비율을 높여서 버튼이 들어갈 공간을 확보해주니 문제가 풀렸습니다.

2.1.2. 버튼 높이 조정

처음에는 장바구니 버튼과 바로구매 버튼이 나란히 배치된다는 것에 기뻐서 다른 것을 보지는 못했는데요.

어느날 찬찬히 살펴보니 두개 버튼의 높이가 미세하게 다르더군요. 이 문제는 어떻게 해결해야할까 고민 고민하다 css로 풀기로 했습니다.

이는 아래와 같은 방법으로 풀었습니다.

1단계, 버튼 class 정의

WooCommerce Direct Checkout Pro를 구입하면 무료 버젼인 WooCommerce Direct Checkout 플러그인과 프로 기능을 추가할 수 있는 WooCommerce Direct Checkout Pro 플러그인 두개를 설치해야 합니다.

아중 모든 세팅은 기본 프로그램이라 할 수 있는 WooCommerce Direct Checkout 플러그인에서 하는데요.

  • 설정 – product로 들어가면 Add quick purchase class라는 항목이 있습니다.
  • 여기에 원하는 class이름을 적어주면 됩니다. 저는 btn btn-primary이라고 적었습니다.
2단계, 장바구니 버튼과 바로구매 버튼의 속성값 확인

브라우저의 요소검사 기능을 통해서 장바구니 버튼과 바로구매 버튼의 속성값을 확인합니다. 그러면 미묘하게 차이나는 점들을 발견하게 되는데요.

저의 경우는 마진과 pedding값이 다를게 적용되어 있더군요. 그래서 그 값을 맞추는 값이 무엇인지 확인합니다.

3단계, css 코드 적용

pedding 등 차이를 맞추어주는 css 코드를 작성해 custom css에 추가합닏.

제가 사용한 css 코드는 아래와 같습니다.

/* 바로구매 버튼 높이 조정*/
.btn-primary {
    display: inline-block;
    font-family: inherit;
    font-size: 12px;
    color: #fff;
    font-weight: 600;
    text-transform: uppercase;
    margin: 0;
    padding: 12px 20px !important;
    border: 0;
    cursor: pointer;
    text-align: center;
    letter-spacing: 0.1em;
    line-height: 1;
    -webkit-transition: all 0.2s ease-in-out;
    -moz-transition: all 0.2s ease-in-out;
    -o-transition: all 0.2s ease-in-out;
    -ms-transition: all 0.2s ease-in-out;
    transition: all 0.2s ease-in-out;
    background-color: #04d39f;
    box-shadow: none;
} Code language: PHP (php)

2.2. 모바일의 One Page Checkout 페이지

데스크탑은 워낙 폭이 넓기 때뭄에 별 문제가 없지만 스마트폰에서는 주문 현황 부문이 스마트폰 한 화면에 그대로 들어오지 않고 스마트폰 폭을 벗어나 버립니다.

[쇼핑몰 구축기] 바로구매 버튼 추가로 구매 단계 줄이기 14

이 문제는 아래와 같은 두가지 방법을 적용해 해결했습니다.

첫번째, 상품 리뷰 정보 항목 중 상품 개별 가격은 총가격이 있기 때문에 빼서 상대적으로 모바일에서도 공간 여유를 갖도록 했습니다.
두번째, 상품 리뷰 정보를 보여주는 테이블의 마진 및 pedding을 최소로 조정해 공간을 확보했습니다.

마진 및 pedding을 조정하가 위한 css코드입니다.

/* 상품 리뷰 테이블 마진 등 최소로 변경하기*/
.woocommerce #order_review table.shop_table{margin:0 0 5px;border:none !important}.woocommerce #order_review table.shop_table th{border:none;padding:5px 5px !important}.woocommerce #order_review table.shop_table td{border:none;padding:3px 5px;text-align:right !important}Code language: PHP (php)

[쇼핑몰 구축기] 메뉴에 로그인/로그아웃 링크 추가하는 다양한 방법

0

이전에 쇼핑몰 메뉴에 우커머스 로그인 및 회워가입 메뉴를 추가하는 방법에 대해서 살펴보았습니다. 이번에는 비슷한 주제이기는 하지만 워드프레스 로그인/로그아웃 링크를 메뉴에 추가하는 다양한 방ㅂ버에 대해서 살펴보겠습니다.

플러그인 없이 메뉴에 워드프레스 로그인 로그아웃 링크 추가하기

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

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

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

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

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

플러그인 없이 메뉴에 워드프레스 로그인 로그아웃 링크 추가하기

이전 포스팅에서 우커머스 내 계정 로그인 및 회원 가입을 메뉴에 추가하는 방법에 대해서 알아봤습니다.

우커머스 내 계정 로그인 및 회원 가입 기능을 이용하는 것도 좋은 선택이지만 워드프레스 본연의 로그인과 회원가입 폼을 이용하는 것도 굉장한 의미가 잇을 것으로 판단합니다.

따라 오늘은 플러그인없이 메뉴에 워드프레스 본연의 로그인 및 로그아웃 잉크 추가 방법에 대해서 살펴보도록 하겠습니다.

우커머스 로그인과 로그아웃 링크는 내 계정 정보를 활용한다는 점에서 효율적이지만 우커머스보다는 워드프레스 중심으로 사이트를 운영하는 경우는 아무래도 워드프레스 로그인과 로그아웃이 더 유용하다고 할 수 있습니다.

1. 워드프레스 로그인 링크 추가하기

워드프레스 로그인 링크를 추가하는 것은 이전에 설명한 우커머스 내 계정 로그인 및 회원 가입 링크를 메뉴에 추가하는 방법과 유사합니다.

  • 로그인 전에는 계정이 있는 경우 로그인, 계정이 없는 경우는 회원가입으로 들어갈 수 있도록 Loh In과 Sign Up의 두개 메뉴가 나타납니다.
  • 로그인 후에는 로그아웃 메뉴만 나타납니다.

이렇게 메뉴에 우커머스 내 계정 로그인/로그아웃 링크를 추가하는 방법은 아래 코드를 function.php 파일에 추가하는 것입니다. 물로 child theme의 function.php에 추가하는 것이 좋겠죠.

add_filter( 'wp_nav_menu_items', 'add_login_logout_register_menu', 20, 2 );
function add_login_logout_register_menu( $items, $args ) {
      if ( $args->theme_location != 'primary' ) {
            return $items;
      }

      if ( is_user_logged_in() ) {
            $items .= '<li><a href="' . wp_logout_url() . '">' . __( 'Log Out' ) . '</a></li>';
      } else {
            $items .= '<li><a href="' . wp_login_url() . '">' . __( 'Login In' ) . '</a></li>';
            $items .= '<li><a href="' . wp_registration_url() . '">' . __( 'Register/Sign Up' ) . '</a></li>';
      }

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

자기 테마 메뉴의 위치 슬러그 찾아 변경하기

여기서도 마찬가지로 메뉴의 위치 슬러그 찾아 변경해야하는데요.

위 코드 중에서 theme_location == ‘primary’)의 ‘primary’가 메뉴의 위치 슬러그를 지칭하고 있습니다. 따라 본인이 사용하는 테마의 메뉴 세팅 부분에서 사용하는 메뉴의 위치 슬러그를 찾아 변경해 주어야 합니다.

이는 메뉴 설정화면에서 크롬의 개발자도구 – 요소 찾기나 파이어폭스의 개발자도구 – 요소 찾기에서 menu-locations 다음에 나오는 위치 슬러그를 찾아 볼 수 있습니다.

아래 이미지는 파이어폭스의 개발자도구 – 검사기에서 메뉴 중 Main을 찍었을 시 검사기에서 나타난 상태인데요. 흐릿한 파란색으로 칠해진 부분을 살펴보면 menu-locations[main menu]를 찾을 수 있죠. 바로 main_menu가 우리가 찾고자하는 메뉴의 위치 슬러그입니다.

[쇼핑몰 구축기] 메뉴에 로그인/로그아웃 링크 추가하는 다양한 방법 15
개발자도구에서 해당 메뉴의 위치 슬러그 찾기

최종적으로는 아래와 같은 코드로 변경됩니다.

add_filter( 'wp_nav_menu_items', 'add_login_logout_register_menu', 20, 2 );
function add_login_logout_register_menu( $items, $args ) {
      if ( $args->theme_location != 'main_menu' ) {
            return $items;
      }

      if ( is_user_logged_in() ) {
            $items .= '<li><a href="' . wp_logout_url() . '">' . __( 'Log Out' ) . '</a></li>';
      } else {
            $items .= '<li><a href="' . wp_login_url() . '">' . __( 'Login In' ) . '</a></li>';
            $items .= '<li><a href="' . wp_registration_url() . '">' . __( 'Register/Sign Up' ) . '</a></li>';
      }

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

2. 보다 근원적인 해결 방안 – 메뉴 로그인 링크

위에서 제시한 방법은 특정메뉴별로 위치 슬러그를 찾아서 기능 정의를 해주어야 합니다.

메뉴를 하나로 통일해서 사용한다면 별 문제가 없겠지만 여러 메뉴에 이러한 로그인 링크를 적용하고 싶다면 메뉴 설정에서 직접 추가가 가능한 방식이 좋을 것입니다.

제가사용 테마의 경우도 일반 PC 접속 상태에서는 정상적으로 로그인 로그아웃 그리고 회원가입이 나타나지만 폭이 좁아져 테블릿이나 모바일 모드가되면 기능이 사라지더군요.

그렇기때문에 보다 근원적으로 메뉴 설정에서 세팅 가능하도록 만들어주는 것이 필요한데요.

아래에서 제시하는 코드는 메우 길지는 하지만 메뉴 설정에서 링크를 추가할 수 있도록 해줍니다.

function.php 파일에 아래 코드를 추가하고 메뉴 세팅에서 링크를 연결해 주면 됩니다. 그러면 아래와 같이 메뉴 설정에서 메뉴에 추가할 수 있는 상태가 됩니다.

워드프레스 로그인 로그아웃  회원가입 랑크를 메뉴에 추가하기

이 방법은 “WordPress Login, Logout Menu Link Without a Plugin”이라는 글에서 소개한 방식입니다.

/********************* START OF THE MENU SNIPPET **********************/

/* To add a metabox in admin menu page */
add_action( 'admin_head-nav-menus.php', 'd2l_add_custommenu_metabox' );
function d2l_add_custommenu_metabox() {
      add_meta_box( 'add-d2l_custommenu', __( 'Register, Log In/Out Links' ), 'd2l_custommenu_metabox', 'nav-menus', 'side', 'default' );
}

/* The metabox code. */
function d2l_custommenu_metabox( $object ) {
      global $nav_menu_selected_id;

      $menukeywords = array(
            '#d2l_login#' => __( 'Log In' ),
            '#d2l_logout#' => __( 'Log Out' ),
            '#d2l_loginout#' => __( 'Log In/Out' ),
            '#d2l_register#' => __( 'Register/Sign Up' ),
            '#d2l_profile#' => __( 'Profile' )
      );

      class d2lCustomMenuItems {
            public $db_id = 0;
            public $object = 'd2l_custommenu';
            public $object_id;
            public $menu_item_parent = 0;
            public $type = 'custom';
            public $title;
            public $url;
            public $target = '';
            public $attr_title = '';
            public $classes = array();
            public $xfn = '';
      }

      $menukeywords_obj = array();
      foreach ( $menukeywords as $value => $title ) {
            $menukeywords_obj[ $title ]                 = new d2lCustomMenuItems();
            $menukeywords_obj[ $title ]->object_id        = esc_attr( $value );
            $menukeywords_obj[ $title ]->title            = esc_attr( $title );
            $menukeywords_obj[ $title ]->url            = esc_attr( $value );
      }
      $walker = new Walker_Nav_Menu_Checklist( array() );
      ?>
      <div id="d2l-custommenu" class="loginlinksdiv">
            <div id="tabs-panel-d2l-custommenu-all" class="tabs-panel tabs-panel-view-all tabs-panel-active">
                  <ul id="d2l-custommenuchecklist" class="list:d2l-custommenu categorychecklist form-no-clear">
                        <?php echo walk_nav_menu_tree( array_map( 'wp_setup_nav_menu_item', $menukeywords_obj ), 0, (object) array( 'walker' => $walker ) ); ?>
                  </ul>
            </div>

            <p class="button-controls">
                  <span class="list-controls">
                        <a href="<?php echo admin_url('/nav-menus.php?d2l_custommenu-tab=all&selectall=1#d2l-custommenu');?>" class="select-all aria-button-if-js" role="button">Select All</a>
                  </span>
                  <span class="add-to-menu">
                        <input type="submit"<?php disabled( $nav_menu_selected_id, 0 ); ?> class="button-secondary submit-add-to-menu right" value="<?php esc_attr_e( 'Add to Menu' ); ?>" name="add-d2l-custommenu-menu-item" id="submit-d2l-custommenu" />
                        <span class="spinner"></span>
                  </span>
            </p>
      </div>
      <?php
}

/* Replace the #keyword# by links */
add_filter( 'wp_setup_nav_menu_item', 'd2l_setup_nav_menu_item' );
function d2l_setup_nav_menu_item( $menu_item ) {
      global $currentpage;

      $menukeywords = array( '#d2l_login#', '#d2l_logout#', '#d2l_loginout#', '#d2l_register#', '#d2l_profile#' );

      if ( isset( $menu_item->object, $menu_item->url )
            && $currentpage != 'nav-menus.php'
            && !is_admin()
            && 'custom'== $menu_item->object
            && in_array( $menu_item->url, $menukeywords ) ) {

            $item_url = substr( $menu_item->url, 0, strpos( $menu_item->url, '#', 1 ) ) . '#';

            $item_redirect = str_replace( $item_url, '', $menu_item->url );

            if( $item_redirect == '%actualpage%') {
                  $item_redirect = $_SERVER['REQUEST_URI'];
            }

            switch ( $item_url ) {
                  case '#d2l_login#'    : $menu_item->url = wp_login_url( $item_redirect ); break;
                  case '#d2l_logout#'   : $menu_item->url = wp_logout_url( $item_redirect ); break;
                  case '#d2l_profile#'   :
                        if( is_user_logged_in() ) {
                              $current_user = wp_get_current_user();
                              $menu_item->title = 'Hi, ' . $current_user->display_name;
                              $menu_item->url = get_edit_user_link($current_user->ID);
                        }else{
                              $menu_item->title = '#d2l_profile#';
                        }
                  break;

                  case '#d2l_register#' :
                        if( is_user_logged_in() ) {
                              $menu_item->title = '#d2l_register#';
                        } else {
                              $menu_item->url = wp_registration_url();
                        }
                  break;

                  case '#d2l_loginout#' :
                        if( is_user_logged_in() ) {
                              $menu_item->title = 'Log Out';
                              $menu_item->url = wp_logout_url();
                        } else {
                              $menu_item->title = 'Log In';
                              $menu_item->url = wp_login_url();
                        }
                  break;
            }

            $menu_item->url = esc_url( $menu_item->url );
      }

      return $menu_item;
}


add_filter( 'wp_nav_menu_objects', 'd2l_nav_menu_objects' );
function d2l_nav_menu_objects( $sorted_menu_items ) {
      foreach ( $sorted_menu_items as $k => $item ) {
            if ( $item->title==$item->url && '#d2l_register#' == $item->title ) {
                  unset( $sorted_menu_items[ $k ] );
            }

            if ( $item->title==$item->url && '#d2l_profile#' == $item->title ) {
                  unset( $sorted_menu_items[ $k ] );
            }

      }
      return $sorted_menu_items;
}

/********************* END OF THE MENU SNIPPET **********************/Code language: PHP (php)