728x90

MVC 패턴

구성

1. Model: 어플리케이션에서 사용되는 데이터와 그 데이터를 처리하는 로직을 담당

2. View: 사용자에게 보여지는 UI 담당

3. Controller: 사용자의 입력을 받고 처리하는 부분

 

동작 방식

View에서 사용자가 액션

-> Controller에 액션이 전달됨, Model이 처리해야될 일이면 Model로 액션을 전달

-> Model이 데이터를 처리

-> Model이 View로 업데이트된 데이터를 전달

 

특징

1. Controller는 여러개의 View를 선택할 수 있는 1:N 관계를 갖음

2. Controller는 View에 직접 데이터를 전달하지는 않음

 

장점

가장 단순하고 많이 쓰이는 보편적인 패턴

 

단점

View와 Model 사이의 의존도가 높음 -> 앱이 커지고 복잡해질수록 유지보수가 어려워짐

 

 

------------------------------------

 

 

MVP 패턴

구성

1. Model: 어플리케이션에서 사용되는 데이터와 데이터를 처리하는 로직을 담당

2. View: 사용자에게 보여지는 UI를 담당

3. Presenter: 사용자의 요청을 처리하고 View와 Model을 연결

 

동작 방식

View에서 사용자가 액션

-> Presenter에 액션이 전달됨, Model이 처리해야될 일이면 Model로 액션을 전달

-> Model이 데이터를 처리 후 Presenter로 응답

-> Presenter가 Model의 응답을 View로 전달

 

특징

1. MVC와 달리 Model과 View과 분리됨

2. 오직 Presenter를 통해서만 상태나 변화를 알려줄 수 있음

3. Prenseter와 View가 1:1 관계를 갖음

 

장점

View와 Model의 의존도를 없앰

 

단점

View와 Presenter의 의존도가 높아져, MVC 패턴의 문제를 그대로 가지고 있음

 

 

------------------------------------

 

 

MVVM 패턴

구성

1. Model: 어플리케이션에 사용되는 데이터와 데이터를 처리하는 로직을 담당

2. View: 사용자에게 보여지는 UI를 담당

3. View Model: View를 처리하기 위해 만든 View를 위한 Model, View를 나타내기 위한 데이터와 데이터를 처리하는 로직을 담당

 

동작 방식

Model을 가지고, Command Design Pattern을 통해 View를 위한 ViewModel을 생성

-> ViewModel 인스턴스를 View에 props로 넘겨줌

-> View와 Model이 Data-binding됨

-> View에서 사용자가 액션

-> View Model에 액션이 전달됨, Model이 처리해야될 일이면 Model로 액션을 전달

-> Model이 데이터를 처리 함

-> Presenter가 Model의 응답을 View로 전달

 

특징

1. Command Design Pattern과 Data-Binding, 두가지 패턴을 사용하여 구현됨

2. View와 View Model, View와 Model 간의 의존도 문제가 위 두가지 패턴으로 해소됨

 

장점

의존성이 사라져 각 부분을 모듈화하여 개발할 수 있음

 

단점

View Model을 잘 설계하는 것이 어려움

728x90
반응형
728x90

개발을 시작할때 한번쯤 공부하지만
세월이 지나면서 잊게되는 HTML 로드 순서.
그치만 웹 개발자라면 잊어서도 안되고 계속해서
공부해야한다.
브라우저는 계속 발전하기 때문에 새로이 업데이트 되는 사항들을 놓치지 말자.

--------------------------------

HTML이 브라우저에 의해 호출되면
기본적으로 위에서 아래의 순서로 파싱된다.

<html>
  <head></head>
  <body>
    <div>
      <p>Hello world</p>
    </div>
  </body>
</html>

위와 같다면 문제없이 위에서 아래로 파싱되며
끝이 난다.

문제는 script 태그를 만났을때다.
브라우저는 HTML을 읽다가 script 태그를 만나면
파싱을 중단하고 script태그를 다운로드 및 파싱한다.

여기서 두가지 문제가 발생한다.

[ 브라우저가 script 태그를 만났을 때, 발생할 수 있는 두가지 문제 ]
1. script 태그가 다운로드 되고 실행되는 것을 기다리느냐, 페이지 로딩이 지연된다.
2. script 태그가 하단의 dom 요소들 보다 먼저 파싱되기 때문에, 
   script 태그에서 dom요소에 접근하여 이벤트 핸들러를 등록하는 등의 행위가 불가능하다.


2번의 문제로 HTML, Javascript를 처음 배울때면
body 가장 밑쪽에 script태그를 배치하라고 배우게된다.


하지만, 그것만으로 모든 문제가 해결될까?
그것은 아니다.
페이지에서 핵심적으로 작용하는 script가 최하단에서 다운로드 및 파싱된다면
사용자가 끔찍한 화면을 본다거나,
에러상황을 마주하게 될 수 있다.

그리고 최신 웹사이트들은
써드파티 라이브러리를 많이 사용하다보니
script 태그를 예전보다 더 적극적으로 많이 사용하게된다.

혹은, 번들러를 통해 빌드하게되면
html에 상당히 많은 script 태그가 자동으로 추가된다.

페이지에 필수적으로 적용되는 script태그를
계속해서 페이지 최하단에 위치시킬것인가?


--------------------------------


그래서 script 태그에는 defer와 async라는 속성이 존재한다.
먼저, defer를 알아보자.

<html>
  <head>
    <script defer src="cdn-subway-tastgood.js" />
  </head>
  <body>
    <div>
      <p>Hello world</p>
    </div>
  </body>
</html>

위 처럼 defer가 적용된 script가 있을때
HTML의 로드 순서는 어떻게될까?

script는 완전히 독립적으로 실행되게 된다.
브라우저는 HTML을 읽다가 script를 만나도
파싱을 중단하지 않고 계속해서 아래로 파싱해나간다.

HTML을 읽다가 script 태그를 마주친 순간부터
script는 백그라운드(Background)에서 다운로드 된다.
defer script의 실행은 domContentLoaded 직전에 실행된다.
(* domcontentloaded 이벤트는 초기 HTML 문서를 완전히 불러오고 분석했을 때 발생하는 이벤트이다.)

따라서, 어느정도 실행 순서가 보장되며
페이지 로딩 속도를 지연시키지 않기 위한 방법이라고 볼 수 있다.


그렇다면 async는 어떨까?

<html>
  <head>
    <script async src="cdn-lotteria-tastgood.js" />
  </head>
  <body>
    <div>
      <p>Hello world</p>
    </div>
  </body>
</html>

async가 적용된 script를 만났을때 역시
파싱이 중단되지 않고 진행된다.
마찬가지로, 백그라운드에서 script가 다운로드된다.

async는 defer와 실행순서에 있어 차이가 있는데,
defer가 domcontentloaded 직전의 실행 시점을 갖는 반면
async는 백그라운드에서 다운로드가 완료된 순가 실행된다.
따라서, async는 그 실행순서가 보장되지는 않는다.

그래서 async는 써드파티 스크립트 중에서도
페이지의 핵심 기능과 독립적인 기능으로 동작하는..
예를들어, 광고나 페이지 방문자수 카운터 등에 사용된다.

728x90
반응형
728x90

테스트 코드를 어떻게 작성해 나갈지에 대해 공부해보고 있다.

 

공부하며 알게된 지식을 정리해본다.

 

NOTE 1.

테스트 코드가 나오려면 명세서가 있어야됨
따라서, 요구사항이 주어지면
1. 요구사항을 이해하는데 시간을 들여 요구의 숨겨진 의도는 없는지 이것으로 달성하고자 하는 목표는 무엇인지
충분히 심사숙고한다.
2. 어떻게 달성할지에 대해서 명세서를 직접 작성해보고 그것에 맞춰 테스트 코드를 작성한 뒤 본 코드를 작성한다.
3. 명세를 작성할때는 어떤 경우에 성공하는지, 혹은 어떤 경우에 실패하는지 모든 가능한 상황을 아울러 작성하도록 하며 기대되는 결과값에 기반해 테스트가 정상인지 확인하면 된다.
728x90
반응형
728x90

1. 변수, 함수명을 보다 구체적이고 직관적으로 작성하라

> 변수, 함수명을 짓는데 시간을 들여 곰곰히 생각해보라.

내가 지금 무엇을 만들려고 하는건지

정확히 인지하고 설계한뒤 이름을 지어야 

내일의 내가, 혹은 동료가 두번 고생 안한다.

 

2. 추상화를 통해 응집도를 높여라

> 먼저, 순수한 함수(pure function)를 작성하도록 항상 노력하자.

순수한 함수는 입력이 같으면 결과도 반드시 같다는 특성과

부수 효과 (side effect)가 발생하지 않아,

함수를 호출한다고 해서 프로그램의 상태가 바뀌는 일이 없다는

특성을 가지고있다.

순수함수를 쓰면 코드 테스트도 쉽고, 함수를 이해하기도 쉽고,

재사용하기도 훨씬 용이해진다.

 

3. 테스트 코드를 작성하도록 노력하라.

> 그러면 자연스럽게 추상화를 하는 습관을 갖게 될 것이다.

테스트 코드를 짜기위해 기능 단위로

나눠서 함수를 작성하게 될것이고,

명확한 함수의 기능에 맞게 함수명을

지을수 있게 된다.

 

4. 초보/주니어 개발자에서 벗어나려면 자료구조, 디자인 패턴을 공부하라

 

5. Frontend 개발자가 중급개발자로 인정받기 위해서는 시간복잡도 향상에 힘을써라

> Backend 개발자와 달리 Frontend 개발자는 초기에

빠르게 실력이 느는것처럼 보이고

3년차와 5년차 7년차가 구현하는 코드에는

큰 차이가 나지않을수 있다.

화면을 그리는데 들어가는 코드는

필연적으로 길게 늘어지고 보기 안좋아보이기 떄문.

다만, 얼마나 효율적으로 동작하는

모델과 컨트롤러를 작성하는지,

상태 관리를 얼마나 효율적으로 하는지에서

Frontend 개발자의 실력이 들어난다.

 

6. 숏코딩에 힘쓰지말자.

> 예를 들어, 메서드 체이닝을 통한 숏코딩은 직관적이라 괜찮지만 삼항연산자를 통해 길게늘어지는 숏코딩보단 if문으로 직관적으로 작성하자.

728x90
반응형
728x90

< 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
반응형
728x90

< 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
반응형
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
반응형
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
반응형
728x90

< 1탄 - 프롤로그 >

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

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

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

 

주로 그 원인은

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

부실하기 때문입니다.

 

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

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

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

 

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

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

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

 

미약하지만

아임포트 같은 서비스없이

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

 

다룰 내용은

Client : React 

server : node js

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

 

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

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

감사합니다.

728x90
반응형
728x90

프로젝트를 진행하다보면

 

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

 

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

 

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

 

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
반응형