본문 바로가기

Node.js

내부함수에서 외부함수로 리턴값 전달하기, 프로미스 리턴하기

js로 프로그램을 짜다보면 어떠한 모듈(주로 async한 작업을 해주는 모듈)을 이용한 함수를 내가 정의했을 때, 리턴값을 원활히 전달하기 쉽지 않은 경우가 자주 있습니다.


● 잘못된 예


예를 들어 어떠한 사이트의 헤더를 긁어오는 작업을 한다고 했을 때

아래의 코드와 같이 작업하면 리턴값을 얻을 수 없습니다.

var request=require("request")

function getHeaders(url){
	request(url, function(err, res, body){
		// console.log(res.headers)
		return res.headers	
		//함수 내부의 request 함수 호출에 전달한 콜백이 완료되면 리턴 값을 전달하고 싶은 것임.
		//하지만 이렇게 하면 예상했던 대로의 리턴값을 얻을 수 없다.
	})
}

console.log(getHeaders("http://senticoding.tistory.com"))


● 해결책 1) Promise이용


우선 async적 작업을 마친 뒤 결과값 리턴을 위해 Promise를 이용할 경우 "함수 작업을 실행 한 뒤 리턴을 해야지"식의 방식이 아닌

"리턴해야지. 이거 끝나고" 이런 식으로 작업을 해야합니다.

무슨 말인지 와닿지 않을 수 있습니다. 쉽게 말하자면

request(url, function( ... ){

... asynce적 작업 ...

return ... 

})     //이런 식으로는 불가능하다


return new Promise(resolve=>{

request(url, function( ... ){

...async적 작업 수행

resolve(리턴값)

})

})    //이런 식으로 해야한다!

먼저 프로미스를 생성하고 프로미스의 콜백에서 async적 작업을 넣어주고 resolve를 해주면 됩니다.


● 해결책 1) 1. Promise를 then()으로 받기


var request=require("request")

function getHeaders(url){
	return new Promise(resolve=>{
		request(url, function(err, res, body){
			// console.log(res.headers)
			resolve(res.headers	)
		})	
	})
	
}

getHeaders("http://senticoding.tistory.com")
	.then(function(result){
	console.log(result)
})

nodejs는 기본적으로 단일 스레드인 프레임웍이지만, then은 쉽게 말해 하나의 스레드를 생성시키는 개념이라고 볼 수 있습니다. 리턴받은 promise를 then속의 콜백 함수에게 인자로 전달해주게 됩니다.


● 해결책 1) 2. Promise를 async await 이용해서 작업하기


var request=require("request")

function getHeaders(url){
	return new Promise(resolve=>{
		request(url, function(err, res, body){
			// console.log(res.headers)
			resolve(res.headers	)
		})	
	})
	
}
(async ()=>{
	console.log(await getHeaders("http://senticoding.tistory.com"))
})()


async/await 는 좀 더 직관적이에요. 우선 await는 말그대로 어떠한 작업을 기다린다는 말입니다. 하지만 await 예약어를 사용하기 위해선 async 함수 안에서만 사용할 수 있다. 처음에는 이 부분이 헷갈릴 수 있습니다. async 함수란 해당 함수가 종료되고서 다음 작업이 이루어 지게되는 함수가 아니라, 해당 함수의 작업을 백그라운드에 넘기고 다음 작업을 수행시키는 함수입니.

즉 async 함수의 작업 자체는 순서에 오히려 구애받지 않는 듯 하지만 함수 내부에서 순서를 좀 더 명확히 하기 위해선 await를 써줘야하는데, 그 함수는  async여야한다는 것입니다.

설명이 좀 장황하긴 했지만, 그냥 쉽게 얘기하자면 await를 쓰기위해선 async함수 내에서만 사용이 가능하다는 것입니다. 따라서 위의 예제 코드에서 arrow function과 즉시 실행 함수 형식을 이용해 간단히 표현해보았습니다.


● 해결책 2) async적 작업을 수행하는 함수가 아닌 sync적 작업을 수행하는 함수를 사용한다.


request 모듈에서는 마땅한 예시가 없지만 주로 쓰이는 fs모듈의 경우에는 예시가 생각이 나 적어볼게요.

예를 들어 fs.writeFile 작업을 수행한뒤 어떠한 값을 리턴해주고 싶은 경우가 있다고 합시다.

fs.writeFile(location, data, function(err){ }) 를 이용하는 경우 위 해결책 1)에서 내가 서술한 대로 하면 됩니다. 

허나 이번엔 해결책 2)로 fs.writeFileSync(location, data)를 이용해볼게요. 즉 writeFile같은 async function이 아닌 sync function을 이용하면 됩니다. 그리고 이게 사실 가장 간단한 방법이에요. ( 그러나 비동기적 처리가 장점인 nodejs 에게 무조건 옳고 좋은 방법은 아닐 수 있음 )


▽코드예제

function saveData(){

fs.writeFileSync("test.txt", "hello")

console.log("in saveData")

return true

}

console.log("before")

saveData()

console.log("after")


● 마치며


사실 C나 JAVA 및 기타 언어를 거치며 이 내용은 Promise 자체가 아니라 그러한 개념을 본 적이 거의 없어서 javascript만의 특이한 개념인 것 같아요. 그래서 js에 익숙치 않으면 어렵게 느껴질 수도 있는데, 위의 내용들만 다 터득해도 js에서 작업의 수행 순서 및 프로미스 리턴 등등에 대해서는 어려운 점은 없을 것이에요. 여러개의 작업을 동기적으로 수행하고 끝으로 Promise.all등을 이용하는 경우가 아닌 한은.