< 5탄 - 모바일 결제 플로우에 맞는 코드리뷰 >

 

이전 글들을 못보신 분들을 위해 앞선 글들의 링크를 제공합니다.

< 1탄 - 프롤로그 >

https://blog.self-made.cloud/240

< 2탄 - Code 먼저 공개합니다. >

https://blog.self-made.cloud/241

< 3탄 - 결제 플로우가 어떻게 되는가 >
https://honeystorage.tistory.com/242 

< 4탄 - 웹 결제 플로우에 맞는 코드리뷰 >
https://honeystorage.tistory.com/243

 

 

아임포트 없이 이니시스 결제모듈 연동하기를 진행중입니다.

그 마지막 여정인대요.

 

이전에도 언급했다싶이

이니시스에서는 웹과 모바일 API를 각각 제공합니다.

따라서, 연동도 각각 나눠서 해주어야 합니다.

 

이번에는 마지막으로

모바일 연동에대해 알아보도록 하겠습니다.

 

1. 클라이언트에서 서버로 상품 및 주문번호 요청
2. 서버에서 주문 상품을 가지고 주문을 생성, 주문 번호를 반환
3. 클라이언트에서 서버로부터 전달받은 주문 번호를 가지고 결제 모듈을 실행함
4. 사용자 액션에 따른 처리 (1. 팝업을 닫음, 2. 팝업에서 결제 요청)
  4-1. 팝업을 닫은 경우, 결제 실패 페이지로 보내거나, 기존의 페이지로 돌려보냄
  4-2. 결제 요청을 완료한 경우, 서버로 요청이 전송됨
5. 서버에서 요청을 처리 및 데이터를 저장하고 결제완료 페이지로 Redirect시킴
6. 클라이언트에서 서버로부터 결제 정보를 호출하여 사용자에게 보여줌

 

1~3 과정을 통해 결제 모듈일 실행합니다.

// client/src/pages/shop

const onOrderRequest = () => {
  if (isMobile()) return mobileOrder();
  else return webOrder();
};

const newOrder = () => {
  return axios.get('/v1/inicis/new/order');
};

const mobileOrder = async () => {
  const { data: info } = await newOrder();
  const { status, data } = info;

  if (status === 'success') {
    orderNumber = data;

    axios.get('/v1/inicis/m/request/form', { params: { orderId: data } }).then(resolve => {
      const { data: info } = resolve;
      const { status, data } = info;

      if (status === 'success') {
        const form = document.createElement('form');
        form.method = 'post';
        form.acceptCharset = 'UTF-8';
        form.hidden = true;
        form.id = 'pay_form';
        form.action = 'https://mobile.inicis.com/smart/payment/';

        for (let o in data) {
          const input = document.createElement('input');
          input.name = o;
          input.value = data[o];
          input.hidden = true;
          form.appendChild(input);
        }

        document.querySelector('#shop-page').appendChild(form);
        form.submit();
      } else {
        alert('요청 실패');
      }
    });
  }
};

 

서버쪽 코드도 살펴볼까요

const getMobileRequestForm = async (req, res) => {
  const { orderId } = req.query;
  if (!orderId) {
    return res.send({ status: 'error', data: 'check parameters' });
  }

  try {
    // 요청된 orderId가 DB에 실제로 존재하는지 체크
  } catch (error) {
    console.log('check exist order: ', error);
    return res.send({ status: 'error', data: 'error' });
  }

  try {
    const price = 100; // 실제로는 요청된 상품 정보를 조회

    const dataset = {
      P_INI_PAYMENT: 'VBANK',
      P_MID: process.env.MID,
      P_OID: orderId,
      P_AMT: price,
      P_GOODS: 'Sample',
      P_UNAME: '홍길동',
      P_NEXT_URL: getServerDomain() + '/v1/inicis/m/pay/after',
      P_NOTI_URL: getServerDomain() + '/v1/inicis/confirm/payment',
      P_HPP_METHOD: 1,
      P_CHARSET: 'utf8', // <-- 결과값 한글깨짐 방지
    };

    return res.send({ status: 'success', data: dataset });
  } catch (error) {
    console.log('make request form : ', error);
    return res.send({ status: 'error', data: 'error' });
  }
};

 

웹 결제보다는 그 연동과정이 훨씬 간단합니다.

이니시스에서 나중에 만든 API라

신경을 더 많이쓴것 같습니다.

 

모바일 연동이라면 처음 진행해도 어렵지않게

진행할수 있지 않을까 싶네요.

 

나머지 과정은 서버로 들어온 결과를 처리하고 데이터를 저장하거나

알맞는 페이지로 사용자를 Redirect 시켜주는 정도인대요.

 

어렵지 않으니 코드를 보며 진행하면 될것같습니다.

 

여기까지 고생 너무 많으셨고요

성공적인 결제연동 되어서

비용 절약하셨기를 바랍니다.

 

다만, 이니시스 뿐만 아니라

여러 PG사를 연동하고자 한다면

개발 시간 비용을 고려했을때

아임포트 같은 서비스를 쓰는게 합리적인 선택이라고

생각이 드네요 ^^

 

728x90
반응형
  1. 감사합니다 2022.01.20 02:15

    리액트 기반의 웹사이트에 한국 서비스들을 올리는 과정이 정말 험난했었는데 덕분에 도움 많이 되었습니다 감사합니다..

    • Websterking 2022.01.20 07:27 신고

      초안만 작성하고 다듬지 못한 미숙한 글 잘 봐주셔서 감사드립니다 ㅎㅎ 좋은 서비스 론칭하시길 기도하겠습니다

< 4탄 - 웹 결제 플로우에 맞는 코드리뷰 >

 

이전 글들을 못보신 분들을 위해 앞선 글들의 링크를 제공합니다.

< 1탄 - 프롤로그 >

https://blog.self-made.cloud/240

< 2탄 - Code 먼저 공개합니다. >

https://blog.self-made.cloud/241

< 3탄 - 결제 플로우가 어떻게 되는가 >
https://honeystorage.tistory.com/242 

 

이니시스 연동을 위한 웹 결제 플로우가 아래와 같다고 3탄에서 소개했는데요.

제법 복잡하죠.. (더 단순화 할수 있을것같지만 더 만지기가 싫더라구요 ㅠㅠ)

먼저, 웹 버전의 플로우는 이와같습니다.

1. 클라이언트에서 서버로 상품 및 주문번호 요청
2. 서버에서 주문 상품을 가지고 주문을 생성, 주문 번호를 반환
3. 클라이언트에서 서버로부터 전달받은 주문 번호를 가지고 결제 폼을 서버로 요청
4. 서버에서 결제를 진행하기위한 데이터들을 클라이언트로 반환
5. 클라이언트에서 서버로 이니시스 결제모듈 요청 (팝업형태)
6. 서버에서 이니시스 결제모듈 반환
7. 클라이언트에 팝업 모듈이 열림 + 이때부터 팝업 모듈 상태를 주기적으로 체크
8. 사용자 액션에 따른 처리 (1. 팝업을 닫음, 2. 팝업에서 결제 요청)
  8-1. 닫은경우 팝업 열람전 페이지 유지 혹은 기타 처리
  8-2. 팝업에서 결제요청한 경우 서버로 요청이 전송됨 + 팝업이 닫힘
9. 서버에서 요청 받은 정보들을 통해 결제요청 처리 및 데이터 저장
10. 팝업 모듈이 닫힌게 체크되면 결제요청 완료 페이지로 이동
11. 클라이언트에서 서버로부터 결제 정보를 호출하여 사용자에게 보여줌

 

 

플로우별로 어떤 코드가 어디에 해당하는지 살펴보도록 하겠습니다.

 

1 ~ 4

// client/src/pages/shop

const onOrderRequest = () => {
  if (isMobile()) return mobileOrder();
  else return webOrder();
};

const newOrder = () => {
  return axios.get('/v1/inicis/new/order');
};

const webOrder = async () => {
  const { data: info } = await newOrder();
  const { status, data } = info;

  if (status === 'success') {
    orderNumber = data;

    axios.get('/v1/inicis/request/form', { params: { orderId: data } }).then(resolve => {
      const { data: info } = resolve;
      const { status, data } = info;
      
      if (status === 'success') {
        const form = document.createElement('form');
        form.method = 'post';
        form.acceptCharset = 'UTF-8';
        form.hidden = true;
        form.id = 'pay_form';

        for (let o in data) {
          const input = document.createElement('input');
          input.name = o;
          input.value = data[o];
          input.hidden = true;
          form.appendChild(input);
        }

        document.querySelector('#shop-page').appendChild(form);
        window.INIStdPay.pay('pay_form');
        inicisFormStatus = setInterval(checkInicisFormStatus, 1000);
      } else {
        alert('요청 실패');
      }
    });
  } else {
    alert('요청 실패');
  }
};

 

// server/api/controllers/inicis.controller.js

const getNewOrder = async (req, res) => {
  const { serviceId } = req.query; // 실제로 개발할때는 요청이 들어온 서비스 정보를 주문번호와 함께 저장합니다.

  try {
    const newOrderId = makeOrderId('sample');

    // save order Id
    // ...

    return res.send({ status: 'success', data: newOrderId });
  } catch (error) {
    console.log('create new order: ', error);
    return res.send({ status: 'error', data: 'error' });
  }
};

const getRequestForm = async (req, res) => {
  const { orderId } = req.query;
  if (!orderId) {
    return res.send({ status: 'error', data: 'check parameters' });
  }

  try {
     // 요청된 orderId가 DB에 실제로 존재하는지 체크
  } catch (error) {
    console.log('check exist order: ', error);
    return res.send({ status: 'error', data: 'error' });
  }

  try {
    const price = 100; // 실제로는 요청된 상품 정보를 조회
    const timestamp = dayjs().valueOf();

    const dataset = {
      version: '1.0',
      gopaymethod: 'VBank',
      mid: process.env.MID,
      signature: encryptSha256(`oid=${orderId}&price=${price}&timestamp=${timestamp}`),
      mKey: encryptSha256('SU5JTElURV9UUklQTEVERVNfS0VZU1RS'), // 개발용, 배포용에서는 발급된 key를 사용
      price,
      oid: orderId,
      timestamp,
      currency: 'WON',
      goodname: 'Sample',
      buyername: '홍길동',
      buyertel: '01012341234',
      buyeremail: 'sample@sample.com.kr',
      returnUrl: getServerDomain() + '/v1/inicis/pay/after',
      payViewType: 'popup',
      popupUrl: getServerDomain() + `/v1/inicis/popup/open/${orderId}`,
      closeUrl: '',
    };

    return res.send({ status: 'success', data: dataset });
  } catch (error) {
    console.log('make request form : ', error);
    return res.send({ status: 'error', data: 'error' });
  }
};

이니시스의 웹 결제요청은 폼을 기본으로합니다.

폼을 기본으로 이니시스에서 제공하는 라이브러리를 통해

결제 API요청을 하기 때문에 위와 같은 처리가 필요합니다.

 

popup 형태로 결제 폼을 띄우기 위해서는 위 코드중

payViewType: 'popup',
popupUrl: getServerDomain() + `/v1/inicis/popup/open/${orderId}`

이 부분이 꼭 필요합니다.

팝업이 아니라 페이지 형태의 결제 폼을 제공한다면 위 두줄은 필요없습니다.

 

클라이언트에 띄워주는 팝업에

알맞는 결제모듈을 제공하기 위한 코드들을 살펴볼까요

 

5~7에 해당합니다.

// server/api/controller/inicis.controller.js

const openInicisModule = async (req, res) => {
  const { orderId } = req.params;
  if (!orderId) {
    return res.send({ status: 'error', data: 'check parameters' });
  }

  try {
    // 요청된 orderId가 DB에 실제로 존재하는지 체크
  } catch (error) {
    console.log('check exist order: ', error);
    return res.send({ status: 'error', data: 'error' });
  }

  try {
    const price = 100; // 실제로는 요청된 상품 정보를 조회
    const timestamp = dayjs().valueOf();

    const dataset = {
      version: '1.0',
      gopaymethod: 'VBank',
      mid: process.env.MID,
      signature: encryptSha256(`oid=${orderId}&price=${price}&timestamp=${timestamp}`),
      mKey: encryptSha256('SU5JTElURV9UUklQTEVERVNfS0VZU1RS'), // 개발용, 배포용에서는 발급된 key를 사용
      price,
      oid: orderId,
      timestamp,
      currency: 'WON',
      goodname: 'Sample',
      buyername: '홍길동',
      buyertel: '01012341234',
      buyeremail: 'sample@sample.com.kr',
      returnUrl: getServerDomain() + '/v1/inicis/pay/after',
      payViewType: 'popup',
      popupUrl: getServerDomain() + `/v1/inicis/popup/open/${orderId}`,
      closeUrl: '',
    };

    return res.render('w_inicis', { ...dataset });
  } catch (error) {
    console.log('make request form : ', error);
    return res.send({ status: 'error', data: 'error' });
  }
};
// server/views/w_inicis.pug

doctype html
html(lang="ko")
  head
    title= title
    meta(http-equiv="Content-Type", content="text/html;charset=UTF-8")
    meta(http-equiv="Cache-Control", content="no-cache")

    meta(property="og:title", content= '이니시스 결제')
    meta(property="og:description", content= '이니시스 결제 연동 해보자')
    meta(property="og:type" content= 'website')

    meta(name="description" content= '이니시스 결제 연동 해보자')
    meta(name="type" content= 'website')

    script(src="https://stgstdpay.inicis.com/stdjs/INIStdPay.js", type="text/javascript", charset="UTF-8")
  body
    script. 
      var onPay = function() { INIStdPay.pay("pay_form") }
      onPay()
    form(id="pay_form", method="post", accept-charset="UTF-8", hidden='true') 
      input(type="hidden", name="version", value= version)
      input(type="hidden", name="gopaymethod", value= gopaymethod)
      input(type="hidden", name="mid", value= mid)
      input(type="hidden", name="oid", value= oid)
      input(type="hidden", name="price", value= price)
      input(type="hidden", name="timestamp", value= timestamp)
      input(type="hidden", name="signature", value= signature)
      input(type="hidden", name="mKey", value= mKey)
      input(type="hidden", name="currency", value= currency)
      input(type="hidden", name="goodname", value= goodname)
      input(type="hidden", name="buyername", value= buyername)
      input(type="hidden", name="buyertel", value= buyertel)
      input(type="hidden", name="buyeremail", value= buyeremail)
      input(type="hidden", name="returnUrl", value= returnUrl)
      input(type="hidden", name="closeUrl", value=closeUrl)

 

결제 연동중 제일 중요한게 위 과정이라고 생각하는데요.

바로, 결제 모듈을 뷰 템플릿을 이용해 SSR(server-side rendering) 형태로 제공하는것입니다.

 

여기서 왜 어려움을 겪게되냐면

이니시스 API상에서 보면 "요청과 응답하는 서버의 Domain을 일치시켜라" 라는 부분이 있거든요.

CSR과 RestAPI를 통해 서비스를 제공하는 입장에선는

이 요구사항을 맞추기가 정말 어렵더라고요.

도대체 이걸 어떻게 해야되나... 많은 생각이 들었습니다.

 

그래서 생각해낸게 바로, "결제 모듈 페이지는 서버에서 제공하자!" 입니다.

CSR프로젝트라고해서 페이지를 모두 클라이언트에서

제공하려고 하는것은 나의 딱딱한 생각 때문구나! 싶었습니다.

 

나머지는 이제 원하는대로 요청을 처리하거나

실패/성공 페이지를 제공하는 정도입니다.

 

https://github.com/jaekwangLee/inicis-without-pg 를

참고하여 진행하면 그리 어렵지않아

웹 결제 플로우 코드리뷰는 여기까지로 마치도록 하겠습니다.

 

728x90
반응형

< 3탄 - 결제 플로우가 어떻게 되는가 >

 

이전 글들을 못보신 분들을 위해 앞선 글들의 링크를 제공합니다.

< 1탄 - 프롤로그 >

https://blog.self-made.cloud/240?category=752264 

< 2탄 - Code 먼저 공개합니다. >

https://blog.self-made.cloud/241?category=752264

 

 

이니시스 결제모듈은

웹 버전과 모바일 버전의 API가 각각 다르게 제공됩니다.

사용해보니 모바일 버전이 나중에 따로 제작된것같네요.

 

아무튼 제공되는 API가 다르니

두가지를 나눠서 설명할 예정입니다.

 

먼저, 웹 버전의 플로우는 이와같습니다.

1. 클라이언트에서 서버로 상품 및 주문번호 요청
2. 서버에서 주문 상품을 가지고 주문을 생성, 주문 번호를 반환
3. 클라이언트에서 서버로부터 전달받은 주문 번호를 가지고 결제 폼을 서버로 요청
4. 서버에서 결제를 진행하기위한 데이터들을 클라이언트로 반환
5. 클라이언트에서 서버로 이니시스 결제모듈 요청 (팝업형태)
6. 서버에서 이니시스 결제모듈 반환
7. 클라이언트에 팝업 모듈이 열림 + 이때부터 팝업 모듈 상태를 주기적으로 체크
8. 사용자 액션에 따른 처리 (1. 팝업을 닫음, 2. 팝업에서 결제 요청)
  8-1. 닫은경우 팝업 열람전 페이지 유지 혹은 기타 처리
  8-2. 팝업에서 결제요청한 경우 서버로 요청이 전송됨 + 팝업이 닫힘
9. 서버에서 요청 받은 정보들을 통해 결제요청 처리 및 데이터 저장
10. 팝업 모듈이 닫힌게 체크되면 결제요청 완료 페이지로 이동
11. 클라이언트에서 서버로부터 결제 정보를 호출하여 사용자에게 보여줌

 

모바일 버전의 플로우

1. 클라이언트에서 서버로 상품 및 주문번호 요청
2. 서버에서 주문 상품을 가지고 주문을 생성, 주문 번호를 반환
3. 클라이언트에서 서버로부터 전달받은 주문 번호를 가지고 결제 모듈을 실행함
4. 사용자 액션에 따른 처리 (1. 팝업을 닫음, 2. 팝업에서 결제 요청)
  4-1. 팝업을 닫은 경우, 결제 실패 페이지로 보내거나, 기존의 페이지로 돌려보냄
  4-2. 결제 요청을 완료한 경우, 서버로 요청이 전송됨
5. 서버에서 요청을 처리 및 데이터를 저장하고 결제완료 페이지로 Redirect시킴
6. 클라이언트에서 서버로부터 결제 정보를 호출하여 사용자에게 보여줌
728x90
반응형

< 2탄 - Code 먼저 공개합니다. >

 

필요한 정보를 위해

시리즈로 제공되는 블로그를 보는것은

어느정도 기본실력이 있는분들에게는

시간낭비 일수있습니다.

 

아임포트와 같은 서비스없이

이니시스 결제 연동을 하고자하는 분들 중에서

 

일부 코드만을 참고하시고자 하는 분들을 위해

관련 소스코드를 모두 업데이트해둔

공개 레포지토리를 먼저 공유합니다.

 

https://github.com/jaekwangLee/inicis-without-pg

 

GitHub - jaekwangLee/inicis-without-pg: CSR환경에서 PG사 없이 KG이니시스 결제연동을 합니다.

CSR환경에서 PG사 없이 KG이니시스 결제연동을 합니다. Contribute to jaekwangLee/inicis-without-pg development by creating an account on GitHub.

github.com

 

728x90
반응형

< 1탄 - 프롤로그 >

웹,앱 등의 서비스에서 광고 수익외에

직접적인 수익을 창출하려면 결제연동을 필수적으로 하게됩니다.

그 과정에서 어려움을 겪게되는대요.

 

주로 그 원인은

PG사들이 제공하는 가이드라인이

부실하기 때문입니다.

 

사실 부실할 수 밖에 없은 이유가있죠.

이제는 너무나도 다양해진 개발 환경에 

모두 대응되는 가이드라인을 제공할 수 없기 때문입니다.

 

이번에 React 프로젝트에 결제연동을 하다보니

react와 같은 CSR에 대해서는 가이드라인이 부실함은 물론

관련 자료도 매우 부족함을 알게되었습니다.

 

미약하지만

아임포트 같은 서비스없이

KG이니시스 결제모듈 연동에 성공하여 정보를 공유하고자합니다.

 

다룰 내용은

Client : React 

server : node js

PG: inicis
결제수단 : 가상계좌 ( 다른 결제수단도 PG사와 제휴만 맺으면 해당 방법으로 얼마든지 응용가능해보입니다.)

 

해당 상황에 해당하시는분들에게

많은 도움이 됐으면 합니다.

감사합니다.

728x90
반응형
  1. ㅇㅇ 2021.11.08 14:43

    아임페이 제외하고는 거의 유일한 React 기반 결제연동 관련글이네요.
    감사합니다...
    백앤드 부분을 spring boot+JSP로, NICEPAY에 적용하려는데 과정이 비슷할까요?

    • Websterking 2021.11.09 14:18 신고

      nicepay는 안써보았지만 백엔드는 무엇으로 하던지 방법론적 면에서는 비슷할것같네요~

      다만, 포스팅을 끝까지 업데이트 못했는데... 실무에서 쓰려면 이후에 더 처리할것들이 있습니다 ㅠㅠㅠ

프로젝트를 진행하다보면

 

점차 규모가 커져서 상대경로로 작업하는데 어려움이 찾아오곤 한다.

 

따라서, 절대경로로 작업을 하고싶을때가 오는데

 

두가지 설정해줄것들이 있다. 

 

1. 절대경로로 표기해도 경로를 자동완성 해줄  "jsconfig.json"

2. 빌드 후에도 해당 경로를 올바르게 파싱해줄 "webpack.config.js"

 

여기서는 jsconfig.json의 설정을 살펴본다

 

아래 링크에서 예제를 확인해볼 수 있다.

 

https://code.visualstudio.com/docs/languages/jsconfig

 

jsconfig.json Reference

View the reference for jsconfig.json.

code.visualstudio.com

 

 

파일경로가

App
- src
  - index.js
  - screens
    - main
      - introduce.js
      - team.js
      - contact.js
    - about.js
  - components
    - common
      - table
        - row.js
        - item.js
- public
  ...

이렇게 되어있을때, team.js에서 row.js를 import 한다고 가정해보자.

import {} from '../../components/common/table/row'

 

지금은 뎁스가 깊지않아 어렵지 않네? 어쩌피 자동완성 지원해주는데 그냥 쓸까? 싶기도 하겠지만

방심은 금물

금방 프로젝트 구조는 복잡해지고말게 분명하다.

 

// jsconfg.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

이렇게 간단한 파일 하나만 App 프로젝트 밑에 추가해주면

import {} from '@/components/common/table/row';

이런식으로 작성하여도  vscode가 자동완성을 지원해준다.

728x90
반응형

브라우저 캐시는 사이트의 속도나 보안에 큰 영향을 미친다.

과거 캐시에 데이터를 때려박는 형태에서는 브라우저 캐시만 탈취하면 사용자 정보를 취득할수도 있었다.

현대에는 간단한 정보는 browser의 localStorage/sessionStorage를 이용해 처리한다.

 

유저 기반 사이트라면 반드시 들어가는 기본이자 가장 중요한 로그인/로그아웃

위 주제는 파도파도 끝이없다.

 

Node.js로 서버 개발을 하는 나는 cache를 이용해서 사용자 정보를 주고받는다.

이때, javascript로는 캐시를 읽어볼수 없게하는 httpOnly옵션과

https에서만 캐시를 캐치가능하게하는 secure옵션을 사용해 보안성을 높인다.

 

사용자 데이터는 JWT를 이용해 토큰화 해서 캐시에 담아 주고받는다.

통신 패킷 자체가 탈취당하더라도 암호화된 데이터는 비밀키가 탈취당하지 않는한 안전하다.

 

그런데, 아주 기본 상식적인 부분에서 문제를 캐치하지 못해 오늘 고생을 하였다.

요점은 이것이다.

naver.com에 로그인하면 m.naver.com  / blog.naver.com / finance.naver.com 등

naver.com의 서브도메인에 해당하는 사이트들에 재 로그인할 필요없이 활동이 가능하다.

그 방법은 브라우저 캐시에 있다. 네트워크 쿠키탭을 뜯어보면 NIDSES (domain: .naver.com)가 포함되어있는것을 알수있다. 각 쿠키가 어떤역할을 하는지 상세한 내용은 알수없으나 분명 사용자 데이터를 포함하고 있고 해당 데이터는 브라우저에 저장된다.

그렇기 때문에 하위 도메인에서도 계정상태를 유지할수 있는것이다.

 

이 부분을 잊어버린 나는 문제아닌 문제와 힘겨운 싸움을 했다.

회사 메인 도메인 사이트에서 로그아웃을 했더니, 서브 도메인 사이트에서도 자꾸만 로그아웃 되는게 아닌가?

이거는 곤란한대?! 하며 속앓이를 했다.

 

어떻게 "잘" 해결할수있는 방법은 없을까? 하고 고민도 해보고 여러가지 시도도 해보았다.

보안이 중요하지 않은 사이트에서는 꼼수는 있지만, 정상적인 사이트에서라면 해당 이슈를 거스리지않는게 맞다는 결론을 내렸다.

728x90
반응형

모바일 반응형의 기본인 viewport메타태그

 

<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=yes" />

maximum-scale을 포함해 다양한 옵션이 존재한다.

 

이중에서 user-scaleable이 yes 상태이면

input창을 focus했을떄 자동으로 화면이 줌인된다.

user-scalable=0 으로 설정하면 해당 이슈는 해결된다

 

다양한 사용자들에게 접근성을 제공하는 옵션이기도하니 서비스의 특성에 맞게 설정하도록하자.

728x90
반응형

지금 운영중인 회사의 사이트가 모바일 가로모드에서 옆으로 돌아가는게 너무나도 싫어서 막는방법에 대해서 알아봤다.

 

1. meta tag설정

2. css 트릭

3. jquery 트릭

4. vanila javascript 설정

5. manifest 설정

 

결론부터 말하면 다 소용없었다.


MDN에 보면 screen orientaion과 device orientation이 다름을 명확히 구분하고있고

screen과 달리 device에 대해서는 별다른 방법이 없는것들만 나타나있다.

 

여기서 screen과 device의 가로모드가 어떻게 다르냐고 한다면

device가 landscape(가로)/portrait(세로)가 돌아감에 따라 screen이 함께 돌아가는데

 

screen은 그에맞게 css를 통해 어느정도 맞춰줄수있지만(@media쿼리 활용)

device가 돌아가는것 그 자체에 대해서는 아직까지는 대응할(막아버릴) 방법이 없어보인다.

 

앱이 내 소유물이라 하면 브라우저는 내 소유물이 아니라 이런 환경적인 부분의 설정에있어서는 한계가 있는걸까?

라는 아쉬움이 남는다.

 

브라우저가 돌아가는것을 막는 좋은 방법을 알고계신분이 있다면, 댓글 달아주시면 감사하겠습니다.

728x90
반응형

종종 캐시로인해 .gitignore의 untracking파일이 제대로 안먹힐때가 있다.

 

이때는 간단히 캐시를 제거해주는것 만으로 해결이 가능하다.

 

git rm -r --cached .

git add .

git commit -m "fix untracked files"

git push

 

commit까지 진행하고나면 untracking파일들이 삭제되는걸 볼수있다.

728x90
반응형