6-17 TIL

hoin_lee·2023년 6월 17일
0

TIL

목록 보기
186/236

Algorithm

공원산책

function solution(park, routes) {
    let height;
    let width;
    for(let i=0; i<=park.length-1;i++) {
        if(park[i].includes("S")){
            height = i
            width = park[i].indexOf("S")
            break;
        }
    }
    for(let i=0; i<=routes.length-1; i++){
        const dir = routes[i][0]
        const dis = Number(routes[i][2]) 
        switch(dir){
            case "N":{
                if(!park[height-dis])continue;
                for(let j=1;j<=dis;j++){
                    if(park[height-j][width]==="X") break;
                    if(j===dis) height-=dis
                }
                break;}
            case "S":{
                if(!park[height+dis])continue;
                for(let j=1;j<=dis;j++){
                    if(park[height+j][width]==="X") break;
                    if(j===dis) height+=dis
                }
                break;}
            case "W": {
                if(!park[height][width-dis]) continue;
                let westLoc = park[height].substr(width-dis,dis)
                if(westLoc.includes("X")) break;
                else width -= dis
                break;}
            case "E": {
                if(!park[height][width+dis])continue;
                let eastLoc = park[height].substr(width+1,dis)
                if(eastLoc.includes("X")) break;
                else width += dis
                break;}
        }
    }
    return [height,width];
}

꽤 길고 복잡한 느낌으로 짜긴 했다. 각 상황을 특정해서 switch-case문으로 방향마다 실행되는 값을 다르게 주고 좀 더 빠른 처리를 위해 if문으로 조건이 맞지 않는 부분을 빠르게 날렸다
결과적으로 시간은 나름 괜찮게 나왔는데 코드적으로 깔끔한지는 애매할 것 같다.
반복문은 break문을 사용하기 쉽게 기본 for문을 사용했다

  • 먼저 "S"의 위치를 반복문을 돌려 찾고 이때 break; 시켜 반복문을 제한한다
  • routes 즉 어떻게 이동할 것인지 반복문을 돌리고 방향과 이동거리를 변수에 넣는다
  • switch-case문으로 방향별로 동작할 코드를 분류한다. 모든 코드는 시작할 때 공원을 넘어갈 경우라는 조건을 파악한 뒤 실행되도록 한다.
  • 상하는 parkindex로 판별하기 위해 반복문을 이용하고 1칸씩 이동할 때 경로에 "X"가 있는지 판별
  • 좌우는 substr을 이용해 이동 거리에 "X"가 있는지 판별한다
  • 이후 모든 조건이 통과 되면 그 값을 맨 처음 지정한 widthheight에 적용

다른사람 풀이

function solution(park, routes) {
        const dirs = { E: [0, 1], W: [0, -1], S: [1, 0], N: [-1, 0] };
        let [x, y] = [0, 0];
        for (let i = 0; i < park.length; i++) {
          if (park[i].includes('S')) {
            [x, y] = [i, park[i].indexOf('S')];
            break;
          }
        }

        routes.forEach((route) => {
          const [r, n] = route.split(' ');
          let [nx, ny] = [x, y];
          let cnt = 0;
          while (cnt < n) {
            [nx, ny] = [nx + dirs[r][0], ny + dirs[r][1]];
            if (!park[nx] || !park[nx][ny] || park[nx][ny] === 'X') break;
            cnt++;
          }
          if (cnt == n) [x, y] = [nx, ny];
        });
        return [x, y];
      }
  • 이동거리를 객체화 시킨다
  • routes를 반복문 돌려 현재 위치를 새로운 변수에 놓고 이동거리를 cnt로 두어 계산한다.
  • 각 이동거리를 객체화 하였기 때문에 통합해서 계산하고 while문으로 이동할 때마다 조건을 판별한 뒤 괜찮다면 마지막에 해당 거리로 이동한 좌표값을 집어 넣는다

CS

Callback Hell이란?

프론트를 공부하다 보면 콜백지옥이란 얘기를 한번쯤 들어봤을 것이다.
비동기 처리 로직을 위해 콜백함수를 연속해서 사용할 때 발생하는 문제인데 예를 들면

$.get('url', function (response) {
    parseValue(response, function (id) {
        auth(id, function (result) {
            display(result, function (text) {
                console.log(text);
            });
        });
    });
});

서버에서 데이터를 받아와 하면에 표시하기까지의 인코딩, 사용자 인증 등을 모두 처리해야 하는 경우가 있는데 만약 이 모든 과정을 비동기로 처리해야 한다면 콜백 안에 콜백을 계속 무는 형식으로 코딩하게 되어 가독성도 떨어지고 로직을 변경하기 힘든상태가 된다.
이 것이 콜백지옥이라고 부른다.

해결법?

일반적으로 콜백 지옥을 해결하는 방법에는 Promise나 Async를 사용하는 방법이 있다.

  • 코딩 패턴으로만 해결
    코딩 패턴으로만 콜백 지옥을 해결하려면 콜백 함수를 분리해주면 된다.
function parseValueDone(id) {
    auth(id, authDone);
}
function authDone(result) {
    display(result, displayDone);
}
function displayDone(text) {
    console.log(text);
}
$.get('url', function (response) {
    parseValue(response, parseValueDone);
});

중첩했던 콜백 익명함수를 각각의 함수로 구분하여 먼저 Ajax 통신으로 받은 데이터를 parseValue()메서드로 파싱한다. parseValueDone()에 파싱한 결과값인 id가 전달되고 auth() 메서드가 실행된다.
auth()메서드로 인증을 거치고 나면 콜백 함수 authDone()이 실행
인증 결과 값인 result로 display()를 호출하면 마지막으로 displayDone()메서드가 수행되며 콘솔에 출력된다.

  • Promise
    JS와 node에서는 주로 비동기 프로그래밍을 하는데 특히 이벤트 주도 방식 때문에 콜백 함수를 자주 사용한다.
    프로미스는 다음과 같은 규칙이 있다.
    먼저 프로미스 객체를 생성해야 한다.
const condition = true; // true면 resolve, false면 reject
const promise = new Promise( (resolve, reject) => {
    if(condition){
        resolve('성공');
    }else{
        reject('실패');
    }
});
 
promise
    .then( (message) => {
        console.log(message); // 성공(resolve)한 경우 실행 
    })
    .catch( (error) => {
        console.error(error); // 실패(reject)한 경우 실행 
});

new Promise로 프로미스를 생성할 수 있으며, 안에 resolve와 reject를 매개변수로 갖는 콜백 함수를 넣어준다.
이렇게 만든 promise변수에 then과 catch메서드를 붙일 수 있고 프로미스 내부에서 resolve가 호출되면 then이 실행되고, reject가 호출되면 catch가 실행된다.

then이나 catch에서 다시 다른 then이나 catch로 붙일 수 있따.

function findAndSaveUser(Users){
    Users.findOne( {}, ( err, user ) => { //첫 번째 콜백
        if(err){
            return console.error(err);
        }
        user.name = 'zero';
        user.save( (err) => { // 두 번째 콜백
            if(err){
                return console.error(err);
            }
        }
        Users.findOne( {gender : 'm'}, (err, user) => { // 세 번째 콜백
            //생략
        });
    });
  });    
}

Async / Await

지금까지 잘 사용하고 있었지만 깊게 파보지 않아 기본적인 개념부터 다시 잡아보자

async/await은 JS 비동기 처리 패턴중 가장 최근 문법으로 기존의 비동기 처리 방식인 콜백 함수와 프로미스의 단점을 보완하고 개발자가 읽기 좋은 코드를 작성할 수 있게 도와준다.

async function 함수명(){
  await 비동기_처리_메서드()
}

async 개념

  • async 키워드는 function 앞에 사용하고 function 앞에 async를 붙이면 해당 함수는 항상 promise를 반환한다. promise가 아닌 값을 반환하더라도 이행 상태의 promise(resolved promise)로 값을 감싸 이행된 프라미스가 반환되도록 한다
async function f(){
	return 1;
}
f().then(alert); //1

위 함수에서 1을 Promise.resolve로 감싸도 같은 결과를 반환

async function f() {
  return Promise.resolve(1);
}

f().then(alert); // 1

await 개념

await는 async 함수 안에서만 동작한다. promise가 처리될 때까지 기다리는 역할을 하고 결과는 그 이후 반환된다.

async function f() {

  let promise = new Promise((resolve, reject) => {
    setTimeout(() => resolve("완료!"), 1000)
  });

  let result = await promise; // 프라미스가 이행될 때까지 기다림 (*)

  alert(result); // "완료!"
}

f();
  • 함수를 호출하고, 함수 본문이 실행되는 도중에 (*)로 표시한 줄에서 실행이 잠시 '중단' 되었다가 promise가 처리되면 실행이 재개
  • promise 객체의 result값이 변수 result에 할당되고 따라서 출력시 1초 뒤에 '완료!'가 출력된다

await는 promise가 처리 될 때까지 함수 실행을 기다리게 만드는 것으로 promise가 처리되면 그 결과와 함께 실행이 재개된다. promise가 처리되길 기다리는 동안에 엔진이 다른 일을 할 수있기에 CPU리소스는 낭비되지 않는다

  • await를 사용하지 않았다면 데이터를 받아온 시점에 콘솔을 출력할 수 있게 콜백함수나 .then()등을 사용해야 했다. 하지만 async await 문법이 나오며 비동기에 대한 사고를 덜해도 된다.

에러제어

await가 던진 에러는 throw가 던진에러를 잡을때처럼 try...catch문을 사용해 잡을 수 있다.

async function f() {

  try {
    let response = await fetch('http://유효하지-않은-주소');
  } catch(err) {
    alert(err); // TypeError: failed to fetch
  }
}

f();

Reference
비동기의 개념
콜백지온이란

profile
https://mo-i-programmers.tistory.com/

0개의 댓글