My ability) HTTP 트랜잭션을 이용한 로그인 창 만들기 - 서버 개설완료,body를 통한 데이터 전달 및 수신

백준우·2021년 10월 30일
0

Ability

목록 보기
3/5
post-thumbnail

이전에 Network에서 Http 통신이 어떻게 이뤄지는지 공부를 했었다. 이를 기반으로 Node.js의 HTTP트랜잭션으로 간단한 로그인창과 서버를 통해 회원 유무를 판별하고 회원일시 OpenAPI를 활용하여 영화리스트를 출력하는 홈페이지를 만들어 보겠다.

최종적으로 필요한 능력은

  1. Http트랙잭션을 이용한 서버 통신
  2. OpenAPI를 통한 데이터 송,수신

1. 로그인 창 구현

페이지는 위와 같이 구현하였다. 이제 서버와 통신하는 방법을 JS파일을 통해 구현하였다.

서버 통신부분

//서버로 보내주는 함수 
function post(Id,PW){
    let Identy = Id
    //fetch를 통해 서버와 통신을 실시 한다.
    fetch(`http://localhost:5000`,{
        method: 'POST', //post metod는 POST로 전달한다.
        body: JSON.stringify(Identy), //body(로그인 정보전달)을 JSON으로 전달한다.
        headers:{
            'Content-Type':'application/json'
        }
    })
        .then(res=> res.json())
        .then(res =>respon(res))
}

서버로 부터 받은 데이터를 처리하는 구간

function respon(result){
    Id_text = ''
    PW_text = ''
    if(result === 'baek1008'){
    alert(`${result}님 로그인이 완료 되었습니다.`)
    }
    else alert('회원이 아니십니다.')
}

TIP) 이때 왜 JSON으로 파일을 전송하냐면 구현기술에 따라 어디든 JSON만 해석한다면 데이터를 사용할수 있으며 이동간의 데이터도 가벼워지기 때문에 통신간의 표준은 JSON을 사용한다.

2. 데이터 처리구간 구현

HTTP트랜잭션 구현간 규칙들이 존재한다.
이러한 규칙들은 https://nodejs.org/ko/docs/guides/anatomy-of-an-http-transaction/ (node.js HTTP트랜잭션 해부)를 확인하면 자세히 알아볼수 있다.

  1. 먼저 서버를 생성하기 위해 웹서버 객체를 만들어야 합니다. 이때 createServr가 사용되며 사용방법은 밑과 같습니다.
const http = require('http');
const server = http.createServer((request, response) => {
  // 여기서 작업이 진행됩니다!
});
  1. 그뒤 request 객체에 우리가 써야할 데이터 값들을 가지고 와야한다. 방법은 구조분해 할당으로 밑과 같이 만들어준다.
 const { headers, method, url } = request;
  1. 요청된 바디는 가지고 오는 방법이 까다롭다. 데이터를 받아올떄 따로따로 받아 배열에 넣어 하나로 합쳐줘야 한다.
    스트림의 'data'와 'end' 이벤트에 이벤트 리스너를 등록해서 데이터를 받을 수 있습니다.
    'data' 이벤트에서 발생시킨 조각은 Buffer입니다. 이 조각이 문자열 데이터라는 것을 알고 있다면 이 데이터를 배열에 수집한 다음 'end' 이벤트에서 이어 붙인 다음 문자열로 만드는 것이 가장 좋습니다.
let body = [];
request.on('data', (chunk) => { //요청된 데이터를 사용할때 .on이라는 메소드를 사용한다.
  body.push(chunk);
}).on('end', () => {
  body = Buffer.concat(body).toString();
  // 여기서 `body`에 전체 요청 바디가 문자열로 담겨있습니다.
});

TIP)왜? createServer을 사용해서 만들어지는게 객체라고 명칭될까?

  • createServer가 반환한 server 객체는 EventEmitter이고 여기서는 server 객체를 생성하고 리스너를 추가하는 축약 문법을 사용한 것이다. 축약을 하지 않으면 원래 아래와 같은 모양이다.
  • 모든 이벤트를 발생시키는 객체는 EventEmitter의 인스턴스이다. 즉, EventEmitter는 인스턴스를 생성하는 클래스라는 뜻이다. 이 객체들은 한 개 이상의 함수를 특정 event에 붙여서 실행될 수 있게 허락하는 .on() 함수를 가지고 있다. 이 객체들이 이벤트를 발생(emit)시키면 그 특정 event에 붙어있는 한 개 이상의 함수들을 전부 비동기적으로 호출시킨다. 호출된 함수들로 인해 발생한 어떠한 리턴 값은 다 무시되고 버려진다.
    다음에 나올 예시는 EventEmitter 클래스를 통해 인스턴스를 생성하고 그 인스턴스들을 통해 .on() 함수를 실행할 것이다.
const EventEmitter = require('events'); // EventEmitter를 쓰기 위해 먼저 설정하는 부분
class MyEmitter extends EventEmitter {} // EventEmitter 클래스를 상속받는 MyEmitter라는 자식 클래스를 생성
const myEmitter = new MyEmitter(); // MyEmitter 클래스를 통해 인스턴스 생성
myEmitter.on('event', () => { // 인스턴스에 on 함수를 적용.
            // event는 이벤트의 이름이고 뒤에 붙는 함수는 이 이벤트에 붙는 함수이다.
  console.log('an event occurred!');
});
myEmitter.emit('event'); // event라는 이벤트에 붙은 함수를 emit로 실행시켜준다.

따라서 createServer가 반환한 server객체는 server.on이 가능하다.

const server = http.createServer((request, response) => {
  // 여기서 작업이 진행됩니다!
});

우리가 사용한 방법인 위 방법은 request,response라는 변수를사용해서 createServer함수를 실행하고 request와 response변수에 (.on)함수를 사용할 수 있게 하는으로 봐도 되겠다.

  1. request된 데이터에 대한 응답헤더를 설정하는 방식은 위와 같다.
response.setHeader('Content-Type', 'application/json');
response.setHeader('X-Powered-By', 'bacon');

TIP)응답헤더를 설정할때 여러번 설정하면 마지막 헤더를 설정합니다.

  1. 명시적인 데이터전송은 헤더를 작성하는 writeHead 메서드가 있습니다. 이 메서드는 스트림에 상태 코드와 헤더를 작성합니다.
//200OK와 그에따른 헤더를 설정해준다. 
response.writeHead(200, {
  'Content-Type': 'application/json',
  'X-Powered-By': 'bacon'
});
  1. body를 전송할때는 write와 end를 사용해 전송해줍니다.
response.write('<html>');
response.write('<body>');
response.write('<h1>Hello, World!</h1>');
response.write('</body>');
response.write('</html>');
response.end();
response.end('<html><body><h1>Hello, World!</h1></body></html>');

TIP)위 두개의 코드는 같은 body를 전송합니다.

위 방법으로 서버가 잘 만들어지는 지 먼저 알아보겠다.

테스트 server

const server = http.createServer((request, response) => {
  const { headers, method, url } = request;
    response.writeHead(200, {'Content-Type': 'application/json'})
  //만들어질 서버에 출력할 문구
    response.end('server complite')
});

출력결과

정상적으로 서버가 만들어졌다. 이제 서버에 데이터를 처리를 하도록 해보겠다.

데이터 처리 구간

//http로 서버를 만들고 사용할 PORT와 IP를 변수로 지정 
const http = require('http');
const PORT = 5000;
const ip = 'localhost';
//서버 객체를 만들어 server 변수에 할당한다.
const server = http.createServer((request, response) => {
  const { headers, method, url } = request;
    //response.writeHead(200, {'Content-Type': 'application/json'})
    //response.end('server complite')
//바디가 비어있지 않으면 밑의 조건문을 실행한다.
if(!(request.body)){
    //body에 요청된 데이터를 한곳으로 모읍니다.
    let body = [];
    request.on('data', (chunk) => {
      body.push(chunk);
    }).on('end', () => {
      body = Buffer.concat(body).toString();   
    //모아진 데이터(body)가 보내집니다.
      response.writeHead(201,{'Content-Type': 'application/json'})
      response.end(body)
    })
}
//body가 비어있거나 서버가 문제일시 다음 404에러를 띄운다.
  else {
      response.writeHead(404, {'Content-Type': 'application/json'})
      response.end() 
  }
//서버 동작시마다 console로 확인한다.
  console.log(
    `http request method is ${request.method}, url is ${request.url}`);
});
//서버 객체를 listen으로 호출한다.
server.listen(PORT, ip, () => {
  console.log(`http server listen on ${ip}:${PORT}`);
});

결과

하지만 결과적으로 회원이 아니라는 창이 출력됩니다. 이말은 서버가 정상작동하였으나 받은 데이터를 처리하는 responeg함수가 제대로 작동하지 않았다는 뜻이다. 그래서 결과 확인을 위해 console을 활용해 보았다.

에러가 많이 뜬다. 이러한 에러부터 처리해보겠다...

해석: <실행 전 요청에 대한 응답이 액세스 제어 검사를 통과하지 못함: 요청된 리소스에 'Access-Control-Allow-Origin' 헤더가 없습니다. 불투명한 응답이 요구 사항을 충족하는 경우 요청 모드를 'no-cors'로 설정하여 CORS가 비활성화된 리소스를 가져옵니다.>

즉, 데이터응답에 대한 CORS가 없어서 위와 같은 에러가 뜨는것이다.

CORS는 이전 블로깅에서도 다뤘던 부분이다. 브라우저에서 자발적으로 실행하는 부분이며 GET과 같은 특정 조건을 모두 만족하는것이 아니면 preflight요청을 실행해서 서버에게 요청 가능여부 확인을 먼저 받고 서버로부터 사용허가 및 사용할 수 있는 메소드를 확인받아 사용하여 데이터를 송수신 하는것이다.

현재 데이터를 보내면 서버측에서는 CORS를 적용하여 현재 데이터를 보내는 URL에 대해 허가유무를 먼저 확인하니 우리는 현재 데이터를 보내는 URL에 대해 허가를 먼저 해야합니다.

헤더를 수정하여야 하며 헤더를 수정하는 방법은 https://nodejs.org/api/http.html#http_response_writehead_statuscode_statusmessage_headers 에서 나오는 예시인 "response.writeHead(status code,statusMessage, headers)"부분을 활용해보겠다.

1.Access-Control-Allow-Origin 사용

에러코드에서 요청하는 Access-Control-Allow-Origin을 사용하면 아래와 갔다.
TIP) Access-Control-Allow-Origin은 응답이 주어진 origin으로부터 요청코드와 공유될수 있는지를 나타냅니다.

EX)브라우저에 https://developer.mozilla.org으로부터의 요청을 허용한다고 알리는 응답은 다음을 포함할 것입니다
Access-Control-Allow-Origin: https://developer.mozilla.org

2.Access-Control-Allow-Headers 사용

Access-Control-Allow-Headers 는 Access-Control-Request-Headers를 포함하는 preflight request의 응답에 사용되는 헤더로, 실제 요청때 사용할 수 있는 HTTP 헤더의 목록을 나열합니다.

그래서 'Access-Control-Allow-Headers': 'Content-Type,Accept'을 포함해 줍니다.
현재 Request부분을 보면 POST메소드를 통해 body에 포함된 데이터가 어떤형식인지 "Content-Type':'application/json"을 전송해서 JSON데이터를 보낼것이라고 알려주었다.
이에따라 Request된 body의 데이터를 파싱할 수 있게 되었으며
최종적으로 response될 데이터의 타입을 표시하게 위해 'Content-Type'을 헤더로 포함해서 전송해야한다.

response.writeHead(201,{'Access-Control-Allow-Headers': 'Content-Type',
			'Access-Control-Allow-Origin': '*',})
그리고 다른 요청에 대한 헤더도 위와같은 방식으로 바꿉니다.

TIP) Content-Type 헤더와 Accept헤더의 차이

  • Content-Type 헤더와 Accept 헤더 둘 다 데이터 타입(MIME)을 다루는 헤더

  • Content-Type: 요청과 응답에 모두 담겨 보내지며 데이터형식을 알려주는헤더,
    서버가 클라이언트에게 "난 이런 Type을 return 할거야" 라는 뜻(GET메소드이 경우 key=value 형식으로 데이터가 전송되기 때문에 'Contet-Type'이 필요없다.

  • Accept: 웹서버로 요청시 요청메시지에 담기는 헤더
    클라이언트가 서버에게 "난 이런데이터만 허용할거야"라는 뜻입니다.

결과

최종적으로 응답이 완료 되었다.

느낀점

  • 서버를 다룰때 header와 메소드를 다양하게 활용해야겠다는걸 깨달았다.
  • 헤더의 종류와 그에따른 역할을 좀더 심도있게 알아볼 필요가 있다.
  • HTTP 트랜잭션 뿐아니라 Express를 활용한 서버통신도 공부해볼 필요가 있다.
  • 필요한 많은양의 HTTP트랜잭션 관련 코드 및 내용중 왜 구동되는지와 필요 여부를 자세히 알 수 있게 되었다.
  • CORS에 유의하여 네트워크 전송을 해야겠다고 깨달았다.

다음계획

  • 서버내에서 회원 유무를 확인 후 회원일시 데이터 전달, 비회원일지 에러를 전달
  • 회원일경우 OpenAPI를 활용해서 응답창에 영화 리스트를 출력

참고

profile
이게 되네?

0개의 댓글