728x90
반응형

1. 미들웨어는 이런식으로 사용이 된다.

// 로그인
router.get('/login', isLoggedIn, (req, res) => {
  res.render('login');
});

 - 라우터가 실행될 때, 그전에 먼저 실행되서 어떻게 할건지를 설정해준다. ( 예를들어 로그인이 안되어있을 경우 이런 페이지로 가겠다. ) 사실 라우터마다 설정들을 다 해줄 순 있지만, 굳이 반복되는 코드를 여러번 쓰는것보단 미들웨어로 한번에 처리하면 사용하기에도 편리하다.

 

먼저, middleware.js라는 파일을 만들고 exports 되는 함수들을 정의한 다음에

const { isLoggedIn, isNotLoggedIn, DataSet, agentDevide } = require('./middleware');

이런 식으로 미들웨어 사용이 필요한 라우터 마다  require로 정의를 해주었다.

 

 

 

2. 나는 이런식으로 로그인이 될 경우에 메인화면으로 오게 만들었고,

exports.isLoggedIn = (req, res, next) => {
  try {
    req.decoded = jwt.verify(req.cookies.token, secretObj.secret);
    res.redirect('/main');
  } catch(err) {
    next();//Token 만료시 다시 로그인 페이지로 넘어가게 설정
  }
};

 

3. 모든 페이지마다 로그인이 안되어있을 경우에 루트 페이지로 가도록 설정했다.

exports.isNotLoggedIn = (req, res, next) => {
  try {
    req.decoded = jwt.verify(req.cookies.token, secretObj.secret);
    next();   
  } catch(err) {
    res.redirect('/') ;//Token 만료시 다시 로그인 페이지로 넘어가게 설정
  }
};
728x90
반응형
728x90
반응형

화면 구성

※ 본사 지점은 무시하셔도 됩니다.

 

1. html 코드

 기본적으로 사업자번호(아이디)와 비밀번호를 입력하도록 설계하였다.

<form action="" method="post" id="login-form2" class="margin-bottom-0" onsubmit="login('login-form2','CNU2','ANA2','PW2');">
<!--사업자번호-->
  <div class="form-group m-b-20">
  <input id="CNU2" name = "CNU2" type="text" class="form-control form-control-lg inverse-mode" placeholder="{{__('business_number')}}" required />
  </div>
<!--비밀번호-->
  <div class="form-group m-b-20">
  <input id="PW2" name = 'PW2' type="password" class="form-control form-control-lg inverse-mode" placeholder="{{__('pw')}}" required />
  </div>
<!--버튼-->
  <div class="login-buttons">
  <button type="submit" class="btn btn-primary btn-block btn-lg">{{__('login')}}</button> 
  <li class="form-control-lg inverse-mode"><a href="/register" >{{__('signup')}}</a>   /  <a href="/find" > {{__('findpw')}}</a></li>
  </div>
</form>

 

2. script부분에서 input을 parsley로 형식을 검사한 후에 loginAjax를 실행하도록 하였다.

function login(form, companyNumber, agentInfo, password) {
    var CNU = document.getElementsByName(companyNumber)[0].value;
    var ANU = document.getElementsByName(agentInfo)[0].value.split("/")[0];
    var ANA = document.getElementsByName(agentInfo)[0].value.split("/")[1];
    var PW = document.getElementsByName(password)[0].value;
    var validateCNU = $('#'+form).parsley();

    if(validateCNU.isValid() == true) {
    	loginAjax(CNU,ANU,ANA,PW);
    }
    event.preventDefault();
  }

 

3. loginAjax() 함수로 백엔드에 데이터를 보낸 후 성공할 경우 어떻게 할건지를 정의한다.

// 로그인 기능
function loginAjax(companyNumber, agentNumber, agentName, password) {
	$.ajax({
		type: 'POST',
		url: 'auth/login',
		dataType: 'json',
		data: {
			CNU: companyNumber,
			ANU: agentNumber,
			ANA: agentName,
			PW: password
		}
	}).done(function(data) {
		if(data.result == 'success') {
			location = '/main';
		}
		else if(data.result == 'fail') {
			alert(i18nconvert('login_error'));
		}
		else {
			alert(i18nconvert('login_fail'));
		}
	});
}

 

3. 백엔드쪽에서 올바른 데이터가 들어왔을 경우 로그인 정보를 쿠키에 담아준다.

//login 진행 할경우 토큰 만들어서 cookie에 넣음
router.post("/login", async(req, res, next) => {
  const {CNU, ANU, ANA, PW} = req.body;
  try {
    const company = await modelQuery(QUERY.Findone,COLLECTION_NAME.Company,{ "CNU" : CNU+ANU, "ANA" : ANA, "ANU" : ANU },{});// CNU에 맞는 데이터 찾아오기
    if(company) {
      //bcrypt 암호화된 PW와 입력 PW 비교
      if(bcrypt.compareSync(PW, company.PW) ){
        const token = jwt.sign({ CNU : company.CNU, ANU : company.ANU, ANA : company.ANA, CNA : company.CNA, CID : company._id, AH : company.AH },// 토큰의 내용(payload)
          secretObj.secret,   // 비밀 키
          { expiresIn: '1440m' });  // 유효 시간은 1440분 하루 설정
        res.cookie("token", token); // 쿠키에 token 등록
        
        return res.send({ result : 'success' });
      }
      else{
        return res.send({ result : "fail" });
      }
    }
    else {
      return res.send({ result : "fail" });
    }
  } catch(err){
    res.send({ result : "fail" });
    console.error(err);
    next(err);
  }
});

 

 

페이지마다 미들웨어를 설정해서 로그인된 정보가 쿠키에 없을 경우에 로그인 화면이나 원하는 화면으로 돌아가도록

설정을 해주면 좋다!

728x90
반응형
728x90
반응형

전 글에서 클라이언트 쪽 데이터 전송까지 다루었다.

이번 글에서는 백엔드쪽을 다루어보겠다.

 

1. 전체 코드( email.js )

//Express
const express = require('express');
const router = express.Router();
//Module
const nodemailer = require('nodemailer');
const bcrypt = require('bcrypt');
//Schemas
const {modelQuery} = require('../schemas/query')
const {COLLECTION_NAME, QUERY} = require('../const/consts');

// -- Start Code -- //

// nodemailer 설정
const smtpTransport = nodemailer.createTransport({
  service: "Gmail",
  auth: {
      user: "사용자 이메일주소(구글)",
      pass: process.env.gmail
  },
  tls: {
      rejectUnauthorized: false
  }
});

// 이메일 전송
router.post('/send', async (req, res, next) => {
  const reademailaddress = req.body.EA;
  
  const exEA = await modelQuery(QUERY.Findone,COLLECTION_NAME.Company,{ "EA" : reademailaddress },{});
  try {
    // 이메일이 중복됐을 때
    if(exEA) {
      return res.send({ result : 'exist' });
    }
    else {
      let authNum = Math.random().toString().substr(2,6);
      const hashAuth = await bcrypt.hash(authNum, 12);
      console.log(authNum);
      res.cookie('hashAuth', hashAuth,{
        maxAge: 300000
      });
      const mailOptions = {
        from: "mk.manager2020@gmail.com",
        to: reademailaddress,
        subject: "OASIS 인증번호 관련 메일 입니다.",
        text: "인증번호는 " + authNum + " 입니다.",
        html: "<div style='font-family: 'Apple SD Gothic Neo', 'sans-serif' !important; width: 540px; height: 600px; border-top: 4px solid #348fe2; margin: 100px auto; padding: 30px 0; box-sizing: border-box;'>"+
              "<h1 style='margin: 0; padding: 0 5px; font-size: 28px; font-weight: 400;'>"+
              "<span style='font-size: 15px; margin: 0 0 10px 3px;'>MK_</span><br />"+
              "<span style='color: #348fe2;'>인증번호</span> 안내입니다."+
              "</h1>"+
              "<p style='font-size: 16px; line-height: 26px; margin-top: 50px; padding: 0 5px;'>"+
              "안녕하세요.<br />"+
              "요청하신 인증번호가 생성되었습니다.<br />"+
              "감사합니다."+
              "</p>"+
              
              "<p style='font-size: 16px; margin: 40px 5px 20px; line-height: 28px;'>"+
              "인증번호: <br />"+
              "<span style='font-size: 24px;'>"+authNum+"</span>"+
              "</p>"+
              "<div style='border-top: 1px solid #DDD; padding: 5px;'>"+
              "</div>"+
              "</div>",
      };
      await smtpTransport.sendMail(mailOptions, (err, res) => {
        if(err) {
          console.log(err);
        } else{
          console.log('success');
        }
        smtpTransport.close();
      });
      
      return res.send({ result : 'send' });
    }
  } catch (err) {
    res.send({ result : 'fail' });
    console.error(err);
    next(err);
  }
});

// 이메일 인증
router.post('/cert', async (req, res, next) => {
  const CEA = req.body.CEA;
  const hashAuth = req.cookies.hashAuth;
  
  try {
    if(bcrypt.compareSync(CEA, hashAuth)) {
      res.send({ result : 'success' });
    }
    else {
      res.send({ result : 'fail' });
    }
  } catch(err) {
    res.send({ result : 'fail' });
    console.error(err);
    next(err);
  }
});

module.exports = router;

 

2. 먼저 nodemailer를 사용하기 전 설정을 해준다.

// nodemailer 설정
const smtpTransport = nodemailer.createTransport({
  service: "Gmail",
  auth: {
      user: "사용자 이메일주소(구글)",
      pass: process.env.gmail
  },
  tls: {
      rejectUnauthorized: false
  }
});

 

3. 그다음 ajax로 보낸 데이터를 받을 라우터를 설정해준다. 먼저 mongoose 쿼리로 이미 가입된 이메일이 있는지를 확인하고, 있을 경우에 랜덤으로 인증번호를 생성해 준 뒤 쿠키에 bcrypt로 암호화된 인증번호를 담는식으로 설정하였다.

이메일 전송이 성공하면 클라이언트쪽에서 인증번호 입력칸, 인증버튼이 생성된다.

 

 

 

이메일을 인증하는 방법은 너무나 다양하다. 각자 본인의 입맛에 맞게 설계하면 된다.

 

 

 

구글로 이메일을 보낼 경우에 구글 계정에서 설정해야 될 것들이 있다.

자세한 내용은 아래 주소 참고바란다.

https://ant-programmer.tistory.com/70

728x90
반응형
728x90
반응형

화면 구성

 

nodemailer 라는 좋은 npm모듈이 있다. 해당 서비스는 클라이언트에서 백엔드(node.js)에 이메일을 보낼거란 신호를 주고, 백엔드 쪽에서 여러 설정( 어떤 메일을 어떤 주소로 보낼건지 등 )을 해놓은 상태에서 메일을 전송하게 되고,

자신이 보낸 메일 인증번호와 클라이언트에서 입력한 인증번호가 같으면 인증이 완료되는 시스템이다.

 

1. 클라이언트 html쪽에선 먼저 이메일 주소 입력창, 인증번호 입력창, 보내기버튼, 인증버튼이 존재한다.

    class 에 d-none 옵션을 추가해서 이메일을 보내지 않은 상태에선 인증번호 입력칸, 인증 버튼이 없도록 했다.

<!--이메일 인증-->
<label class="control-label">{{__('email')}} {{__('auth')}}<span class="text-danger"> *</span> </label>
  <div class="form-group m-b-20">
    <input id="EA" name="EA" type="email" class="form-control form-control-lg inverse-mode" value ="{{email}}" placeholder="{{__('email')}}" data-parsley-error-message=null required >
    <input id="CEA" name="CEA" type="text" class="form-control form-control-lg inverse-model mt-3 d-none" placeholder="{{__('authnum')}}" data-parsley-error-message="{{__('required_detail')}}" data-parsley-errors-container="#err-msg2" required >
  <div class="d-flex justify-content-between align-items-center mt-2">
  <div id="err-msg2" class="text-realred"></div>
    <input type="button" id="sendBtn" class="btn btn-primary btn-block btn-lg ml-auto custombtn" value="{{__('send')}}" onclick="emailSend('EA')">
    <input type="button" id="cerBtn" class="btn btn-primary btn-block btn-lg mt-0 ml-auto custombtn d-none" value="{{__('auth')}}" onclick="emailCer('CEA')">
    <input type="hidden" id="hideCK" name="hideCK" value="">
  </div>
</div>

 

2. 클라이언트 javascript 데이터 검증부분에선 먼저 버튼에 onclick에 붙어있는 함수들을 선언한다. parsley를 사용해 input에 제대로 된 데이터가 들어갔는지 먼저 검증하고 맞으면 아래 함수를 실행하도록 되어있다. 

	// 이메일 전송 기능
	function emailSend(email) {
		var EA = document.getElementsByName(email)[0].value;
		var validateEA = $('#'+email).parsley();
		
		if(validateEA.isValid() == true) {
			emailSendAjax(EA);
		}
		else {
			return alert("{{__('register_email_valid_msg')}}");
		}
	}
	
	// 이메일 인증 기능
	function emailCer(cerNum) {
		var CEA = document.getElementsByName(cerNum)[0].value;
		var validateCEA = $('#'+cerNum).parsley();
		
		if(validateCEA.isValid() == true) {
			emailCerAjax(CEA);
		}
		else {
			alert("{{__('register_auth_enter')}}");
		}
	}

 

3. 클라이언트 javascript 함수 실행부분에선 검증된 데이터가 들어왔을 때, ajax로 백엔드에 데이터를 전송하도록 하였다.

// 이메일 전송 기능
function emailSendAjax(email) {
    $.ajax({
		type: 'POST',
		url: '/email/send',
		dataType: 'json',
		data: {
			EA: email
		}
	}).done(function(data) {
		if (data.result == 'exist') {
			alert(i18nconvert('register_already_msg'));
		}
		else if (data.result == 'send') {
			alert(i18nconvert('register_auth_com_msg'));
			document.getElementsByName('EA')[0].readOnly = true;
			document.getElementById('CEA').classList.remove('d-none');
			document.getElementById('cerBtn').classList.remove('d-none');
			document.getElementById('sendBtn').classList.add('d-none');
			
			clearTimeout(timer);
			stopWatch(300);
		}
		else {
			alert(i18nconvert('register_email_send_fail'));
		}
	});
}

// 이메일 인증 기능
function emailCerAjax(cerNum) {
    $.ajax({
		type: 'POST',
		url: '/email/cert',
		dataType: 'json',
		data: {
			CEA: cerNum
		}
	}).done(function(data) {
		if (data.result == 'success') {
			alert(i18nconvert('register_auth_success'));
			clearTimeout(timer);
			document.getElementsByName('CEA')[0].readOnly = true;
			document.getElementById('err-msg2').innerHTML = i18nconvert('register_auth_success');
			document.getElementsByName('hideCK')[0].value = 'true';
		}
		else {
			alert(i18nconvert('register_auth_fail'));
			document.getElementsByName('CEA')[0].value = null;
			document.getElementsByName('hideCK')[0].value = null;
		}
	});
}

 

백엔드 쪽은 다음 글에서 설명하겠다.

 

@ 인증번호 타이머 기능

   setInterval 함수를 사용해 1초에 한번씩 바꿔주었다.

function stopWatch(TimeSet) {
	timer = setInterval(function(){
		sec = (TimeSet)%60;
		document.getElementById('err-msg2').innerHTML =i18nconvert('register_auth_time') + '&nbsp;' + parseInt(TimeSet/60) + i18nconvert('register_auth_minute') + sec + i18nconvert('register_auth_second') + "." + "<br><a href='javascript:;' id='resend' class='text-white' onclick=emailSend('EA')><u>"+i18nconvert('register_auth_resend')+"</u></a>";
		TimeSet--;
		
		if(TimeSet < 0){
			clearTimeout(timer);
			alert(i18nconvert('register_auth_timeout'));
			document.getElementsByName('EA')[0].value = null;
			document.getElementsByName('CEA')[0].value = null;
			document.getElementsByName('EA')[0].readOnly = false;
			document.getElementsByName('CEA')[0].readOnly = false;
			document.getElementsByName('hideCK')[0].value = null;
			document.getElementsByName('hideCNU')[0].value = null;
			document.getElementById('CEA').classList.add('d-none');
			document.getElementById('cerBtn').classList.add('d-none');
			document.getElementById('sendBtn').classList.remove('d-none');
			document.getElementById('err-msg2').innerHTML = i18nconvert('register_reauth_msg');
		}
    }, 1000);
}
728x90
반응형
728x90
반응형

백엔드(Node.js) 라우터 /address 에 address.html 파일을 렌더링하게 만들고

버튼을 눌렀을 경우 팝업창을 띄우도록 만들겠다.

 

예시

 

1. 먼저 html 코드는 아래와 같다.  addr1 부분은 검색을 한 뒤 자동으로 채워줄 부분이기 때문에 readonly를 추가하였고 searchaddr()을 onclick에 추가하였으며, 상세주소는 addr2에 추가할 수 있도록 하였다.

<!--주소-->
<label class="control-label">{{__('address')}}<span class="text-danger"> *</span> </label>
  <div class="form-group m-b-20">
    <div class="d-flex justify-content-between align-items-center mb-2">
      <input id="addr1" name="addr1" type="text" class="form-control form-control-lg inverse-mode w-70 mr-2" value ="{{addr1}}" placeholder="{{__('address_1')}}" data-parsley-error-message=null readonly required >
      <input type="button" class="btn btn-primary btn-block btn-lg w-25" value="{{__('search')}}" onclick="searchAddr();">
    </div>
    <input id="addr2" name="addr2" type="text" class="form-control form-control-lg inverse-model" value ="{{addr2}}" placeholder="{{__('address_2')}}" data-parsley-error-message="{{__('required_detail')}}" required >
  </div>

 

2. searchAddr() 함수는 아래와 같다. /address 팝업창을 띄어주고 주소를 받는 콜백함수를 지정했다.

// 주소 검색 기능
function searchAddr(){
	// 호출된 페이지(jusopopup.jsp)에서 실제 주소검색URL(https://www.juso.go.kr/addrlink/addrLinkUrl.do)를 호출하게 됩니다.
    var pop = window.open("/address","pop","width=570,height=420, scrollbars=yes, resizable=yes"); 
    
	// 모바일 웹인 경우, 호출된 페이지(jusopopup.jsp)에서 실제 주소검색URL(https://www.juso.go.kr/addrlink/addrMobileLinkUrl.do)를 호출하게 됩니다.
    //var pop = window.open("/popup/jusoPopup.jsp","pop","scrollbars=yes, resizable=yes"); 
}
/** API 서비스 제공항목 확대 (2017.02) **/
function jusoCallBack(roadFullAddr,roadAddrPart1,addrDetail,roadAddrPart2,engAddr, jibunAddr, zipNo, admCd, rnMgtSn, bdMgtSn
						, detBdNmList, bdNm, bdKdcd, siNm, sggNm, emdNm, liNm, rn, udrtYn, buldMnnm, buldSlno, mtYn, lnbrMnnm, lnbrSlno, emdNo){
	// 팝업페이지에서 주소입력한 정보를 받아서, 현 페이지에 정보를 등록합니다.
	document.getElementsByName('addr1')[0].value = roadAddrPart1+roadAddrPart2;
	document.getElementsByName('addr2')[0].value = addrDetail;
}

 

3. /address 랜더링 부분 (node.js) 에선 먼저 get 메소드로 승인키를 보내고, post 메소드로 입력한 주소를 담게 하도록 구현하였다. ( address_pop.html로 랜더링 하였다. )

router.get('/address', (req, res) => {
  const juso = process.env.juso;
  res.render('address_pop', {juso});
});

router.post('/address', (req, res) => {
  const juso = process.env.juso;
  const locals = req.body;
  
  res.render('address_pop', {juso, locals});
});

 

4. addresspop.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<title>Juso Search</title>
</head>

<script language="javascript">
//opener관련 오류가 발생하는 경우 아래 주석을 해지하고, 사용자의 도메인정보를 입력합니다. ("주소입력화면 소스"도 동일하게 적용시켜야 합니다.)
//document.domain = "abc.go.kr";

function init() {
	var url = location.href;
	var confmKey = "{{juso}}";//승인키
	// resultType항목 추가(2016.10.06)
	var resultType = "4"; // 도로명주소 검색결과 화면 출력유형, 1 : 도로명, 2 : 도로명+지번+상세보기(관련지번, 관할주민센터), 3 : 도로명+상세보기(상세건물명), 4 : 도로명+지번+상세보기(관련지번, 관할주민센터, 상세건물명)
	
	var inputYn = '{{locals.inputYn}}';

	if(inputYn != "Y") {
		document.form.confmKey.value = confmKey;
		document.form.returnUrl.value = url;
		document.form.resultType.value = resultType; // resultType항목 추가(2016.10.06)
		document.form.action="https://www.juso.go.kr/addrlink/addrLinkUrl.do"; // 인터넷망
		//document.form.action="https://www.juso.go.kr/addrlink/addrMobileLinkUrl.do"; //모바일 웹인 경우, 인터넷망
		document.form.submit();
	}
	else {
		var roadFullAddr = '{{locals.roadFullAddr}}';
		var roadAddrPart1 = '{{locals.roadAddrPart1}}';
		var addrDetail = '{{locals.addrDetail}}';
		var roadAddrPart2 = '{{locals.roadAddrPart2}}';
		var engAddr = '{{locals.engAddr}}';
		var jibunAddr = '{{locals.jibunAddr}}';
		var zipNo = '{{locals.zipNo}}';
		var admCd = '{{locals.admCd}}';
		var rnMgtSn = '{{locals.rnMgtSn}}';
		var bdMgtSn = '{{locals.bdMgtSn}}';
		/** API 서비스 제공항목 확대 (2017.02) **/
		opener.parent.jusoCallBack(
			roadFullAddr,
			roadAddrPart1,
			addrDetail,
			roadAddrPart2,
			engAddr,
			jibunAddr,
			zipNo,
			admCd,
			rnMgtSn,
			bdMgtSn
		);
		window.close();
	}
}



</script>

<body onload="init();">
	<form id="form" name="form" method="post">
		<input type="hidden" id="confmKey" name="confmKey" value=""/>
		<input type="hidden" id="returnUrl" name="returnUrl" value=""/>
		<input type="hidden" id="resultType" name="resultType" value=""/> 
		<!-- 해당시스템의 인코딩타입이 EUC-KR일경우에만 추가 START--> 
		<!-- 
		<input type="hidden" id="encodingType" name="encodingType" value="EUC-KR"/>
		 -->
		<!-- 해당시스템의 인코딩타입이 EUC-KR일경우에만 추가 END-->
	</form>
</body>
</html>

 

 

react에서는 react-daum-post라는 node모듈이 있는데, 이렇게 Jquery에서 힘들게 구현해보고 리액트의 라이브러리가 괜히 편한게 아니라는걸 깨달았다. (적어도 이 방식보단 쉽다고 느꼈다.) 참고로 JQuery에서 팝업을 띄우는 이 방식은 https가 아닌 http에서는 안전하지 않은 사이트라며 "무시하고보내기" 라는 버튼을 눌러야 실행된다. 사용자 입장에서 불안할 수도 있다고 생각하였다.

728x90
반응형
728x90
반응형

화면 구성

 

1. JQuery

ajax로 node 서버에 CNU값을 보낸다.

$.ajax({
		type: 'POST',
		url: '/company/checkCNU',
		dataType: 'json',
		data: {
			CNU : CNU
		}
	})
}

 

2. Node.js

사업자 휴폐업 조회 오픈 API를 사용했다.

JQuery에서 받아온 데이터를 가져와 직접 만든 postCRN이란 함수에 인자로 넣어서 실행해준다.

사업자 번호가 올바르면 "부가가치세 일반과세자 입니다."로 뜨기에, 올바른 사업자번호일 경우 true값을 보내도록

하였다. 이후에 res.send를 이용하여 클라이언트쪽에서 판별하기로 하였다.

// 사업자등록번호 검증
router.post('/checkCNU', async (req, res, next) => {
  const CNU = req.body.CNU;
  const CNU_CK = await postCRN(CNU);
  
  // Company Number check
  async function postCRN(crn){
    const postUrl = "https://teht.hometax.go.kr/wqAction.do?actionId=ATTABZAA001R08&screenId=UTEABAAA13&popupYn=false&realScreenId=";
    const xmlRaw = "<map id=\"ATTABZAA001R08\"><pubcUserNo/><mobYn>N</mobYn><inqrTrgtClCd>1</inqrTrgtClCd><txprDscmNo>{CRN}</txprDscmNo><dongCode>15</dongCode><psbSearch>Y</psbSearch><map id=\"userReqInfoVO\"/></map>";
      try {
        const result  = await axios.post(postUrl,xmlRaw.replace(/\{CRN\}/, crn),
          { headers: { 'Content-Type': 'text/xml' } });
        let CRNumber = await getCRNresultFromXml(result.data);
        
        if (CRNumber ==='부가가치세 일반과세자 입니다.') {
          CRNumber = true;
        } else {
          CRNumber = false;
        }
        
        return res.send({ CRNumber : CRNumber });
        
      } catch(err){
        console.error(err);
        next(err);
      }
          
  }
  
  function getCRNresultFromXml(dataString) {
    return new Promise((resolve, reject) => {
      xml2js.parseString(dataString, // API 응답의 'data' 에 지정된 xml 값 추출, 파싱
        (err, res) => {
          if (err) reject(err);
          else resolve(res.map.trtCntn[0]); // trtCntn 이라는 TAG 의 값을 get
        });
    });
  }
});

 

 

3. JQuery

 

따라서 결과값이 true일 경우, false일 경우에 각각 원하는 작업이 이루어지도록 구현했다.

// 사업자 번호 조회 및 정보 가져오기 기능
function checkCNU(companyNumber) {
	var CNU = document.getElementsByName(companyNumber)[0].value;
	$.ajax({
		type: 'POST',
		url: '/company/checkCNU',
		dataType: 'json',
		data: {
			CNU : CNU
		}
	}).done(function(data) {
		if(data.CRNumber) {
		    alert(i18nconvert('register_business_right'));
		    document.getElementsByName('hideCNU')[0].value = 'true';
		    document.getElementById('err-msg-cnu').innerHTML = i18nconvert('register_company_cer_success');
		    $("input[name='CNU']").prop('readonly', true);
		    $('.agent-select').removeClass('d-none');
		}
		else {
			alert(i18nconvert('register_business_noexist'));
			document.getElementsByName("CNA").value = null;
			document.getElementsByName("NA").value = null;
			document.getElementsByName("MN").value = null;
			document.getElementsByName("PN").value = null;
			document.getElementsByName("addr1").value = null;
			document.getElementsByName("addr2").value = null;
			document.getElementsByName('hideCNU')[0].value = null;
			document.getElementById('err-msg-cnu').innerHTML = i18nconvert('register_company_need_cer');
			$("input[name='CNU']").prop('readonly', false);
		    $('.agent-select').addClass('d-none');
		}
	});
}

 

728x90
반응형
728x90
반응형

사이트의 가장 기본이되는 회원가입 화면!!

 

Node.js 와 JQuery로 만드는 사이트 제 1단계인 로그인 만들기를 시작해보겠다.

1년동안 웹페이지를 개발한 결과 CRUD가 괜히 이 순서가 아닌거 같았다. 테이블, 계정관리, 대시보드 등 모든 것이 

등록 - 조회 - 수정 - 삭제 이순서대로 개발하면 문제가 될 것 없이 편했다.

 

1. 화면은 이런식으로 하였다. ( 본사, 지점은 무시하셔도 됩니다 )

    기타 

다른 부수적인 내용들( 특별한 기능이 없는 )을 제외한 중요 기능 위주로 설명하겠다.

728x90
반응형
728x90
반응형

node js 홈페이지 메인

 

1. 업데이트를 실행해준다.

sudo apt-get update

 

2. 업그레이드 해준다.

sudo apt-get upgrade

 

3. npm을 설치한다.

sudo apt-get install npm

 

4. n을 설치한다.

sudo npm install n -g

 

5. 설치한 n을 이용해 node.js를 설치한다.

sudo n 18.4.0

 

6. 버전을 확인해본다.

sudo n

 

7. helloworld를 만들어준다.

cd ~
mkdir helloworld

 

 

 

 

 

 

 

 

 

728x90
반응형
728x90
반응형

 

1. 전에 설명했듯이 서버 인스턴스 사용자 계정(ubuntu)의 권한을 설정해준다.

sudo chown -R ubuntu *

 

2. openssh-server를 설치해주고, sshd 서비스를 실행해준다.

sudo apt-get install openssh-server & sudo service sshd start

 

3. ssh폴더로 이동하고, sshd config 파일을 수정해준다.

cd /etc/ssh/
vi sshd_config

 

4. 아래와 같이 설정해준다.

ClientAliveInterval 0
ClientAliveCountMax 0

ClientAliveInterval 60
ClientAliveCountMax 5

위와 같이 설정을 하게 되면 60초마다 클라이언트로부터 입력값이 없는지 확인하게 됩니다.

이 과정을 총 5번 하게 되는데 즉, 60 * 5 = 5분 후에는 연결이 끊기게 됩니다.

 

5. 이후에 :wq! 명령어로 파일을 저장후 나와서 ssh 서비스를 재시작합니다.

serivce sshd restart

 

 

6. VScode 파일 - 기본설정 - 설정에 들어가 다음과 같이 설정해준다. ( 시간은 원하는대로! )

 

7. 전전글에 써있던 대로 remote-ssh로 연결을 시도해보면 성공!

 

 

 

 

 

 

 

728x90
반응형

'Server > AWS' 카테고리의 다른 글

AWS VScode 연결하기(2) - ssh-key  (0) 2021.09.10
AWS VScode 연결하기(1)  (0) 2021.09.10
AWS LightSail(2) - 고정 IP 만들어주기  (0) 2021.09.10
AWS LightSail(1) - 인스턴스 생성하기  (0) 2021.05.18
728x90
반응형

전 글에 이어서 사용자계정과 key 파일에 관하여 설명하겠다.

우분투 인스턴스를 만들면, root계정과 사용자 계정이 생기게 되는데, 사용자 계정을 여러개 만들고 계정에 따른

권한을 설정해줄 수 있다.

 

인스턴스를 VScode로 원격서버접속을 하게되면 당연히 권한이 제한되어 파일 수정, 저장이 불가능하게 된다.

때문에 인스턴스자체에서 사용자 계정에 권한을 부여해야한다.

또한 아무리 사용자 계정 이름과 IP주소를 안다 해도 아무 곳에서나 접속을 하면 안되기 때문에 이를 인증하기 위해

원격서버접속을 할 때 내가 인스턴스를 만든 사람이란걸 증명해야되는데 그 때 필요한 것이 ssh-key이다.

해당 리전에 대한 key가 있으면 되는데, 이 글은 Lightsail의 경우에 대해 설명하겠다.

 

1. 먼저 Lightsail 홈 상단에 계정-계정을 들어가준다.

 

2. ssh키 탭에 들어가서 인스턴스가 존재하는 리전의 key를 다운받아준다.

 

3. 그럼 이런식으로 생기게 되는데, 위 사진에서 보이는 것과 같이 (ap-northeast-2) <-- 이 이름에 맞는 키 파일을 기억해둔다.

 

4. 현재 이 pem 파일은 초기 권한 설정이 되어있기 때문에 컴퓨터 사용자만 가능하게하도록 권한을 제한해야된다.

( 이 과정을 안해주면 연결을 할 시에 오류가 뜬다. )

 

5. pem파일을 마우스 오른쪽 버튼으로 클릭하고 속성-보안-고급을 눌러준다.

 

6. 그럼 이 탭이 뜨게 되는데,

  좌측 하단의 '상속 사용 안함'-> '이 개체에서 사용된 사용 권한을 모두 제거합니다.' 를 클릭한다.

 

7. 그 다음 좌측 하단의 "상속 사용 안함" 버튼위에 추가를 누르고 보안 주체를 선택해 컴퓨터 사용자의

    이름을 적고 (제어판에서 확인 가능) 확인 버튼을 누른다.

 

이상으로 키 파일을 다운받고 키 파일의 권한을 설정해주었다.

728x90
반응형

'Server > AWS' 카테고리의 다른 글

AWS VScode 연결하기(3) - VScode  (0) 2021.09.10
AWS VScode 연결하기(1)  (0) 2021.09.10
AWS LightSail(2) - 고정 IP 만들어주기  (0) 2021.09.10
AWS LightSail(1) - 인스턴스 생성하기  (0) 2021.05.18

+ Recent posts