본격적으로 node.js에 대해서 공부를 시작했다.
간단하게 node.js의 환경을 잡는 법, mongo db와 연동하고 express를 사용하여 api를 만드는 방법에 대해서 공부를 했다.
예제로 쇼핑몰에서 사용하듯이
물건 목록을 goodsId값으로 추가하고, 불러오고
또 그러한 목록중에서 장바구니 안에 물건을 추가하는 등의 작업을 구현했다.
(goods자체와 관련된 기능 + goods를 사용하여 cart와 관련된 기능)
작업을 하면서 goods.js와 cart.js를 분리하여 작업을 했는데, 퀴즈로 나온 상황이 분리되었던 cart의 정보 조회 기능을 goods.js 내부로 합치는 내용이었다.
//goods.js의 기능 구현내용(일부)
const Goods = require("../schemas/goods.js");
router.get("/goods", (req, res) => {
res.status(200).json({ goods });
});
router.get("/goods/:goodsId", (req, res) => {
const { goodsId } = req.params;
const [result] = goods.filter((good) => Number(goodsId) === good.goodsId);
res.status(200).json({ detail: result });
});
router.post("/goods/", async (req, res) => {
const { goodsId, name, thumbnailUrl, category, price } = req.body;
const goods = await Goods.find({ goodsId });
if (goods.length) {
return res.status(400).json({
succcess: false,
errorMessage: "이미 존재하는 GoodsId입니다.",
});
}
const createGoods = await Goods.create({
goodsId,
name,
thumbnailUrl,
category,
price,
});
res.json({ goods: createGoods });
});
그리고 아래는 cart.js에 있던 cart의 조회기능 부분이다.
router.get("/carts", async (req, res) => {
const carts = await Cart.find();
const goodsIds = carts.map((cart) => cart.goodsId);
const goods = await Goods.find({ goodsId: goodsIds });
res.json({
carts: carts.map((cart) => ({
quantity: cart.quantity,
goods: goods.find((item) => item.goodsId === cart.goodsId),
})),
});
});
여기서 router.get("/carts", async(req,res) => {생략} )
의 주소 부분을 goods와 연관되었다는걸 확실하게 보여주기 위해서 "/goods/cart"로 변경하면서 파일을 합치는게 주 목적이었다.
해당 코드를 goods.js로 옮기고 없는 변수를 추가해주거나 필요없어진 것들을 제거해주었는데
Thunder Client로 get요청을 보내보았으나 db에 저장된 값이 있음에도 불구하고 {}값을 반환했다.
혹시나 하여 다시 합치기 전으로 돌려서 작동을 해보았더니 제대로 저장된 값을 불러오는걸 볼 수 있었다.
한참을 찾다가 의심가는 부분을 발견했는데,
goods의 get으로 조회하는 기능 중에,
goodsId값으로 목록 내에서 조회하는 기능이 있었다.
router.get("/goods/:goodsId", (req, res) => {
const { goodsId } = req.params;
const [result] = goods.filter((good) => Number(goodsId) === good.goodsId);
res.status(200).json({ detail: result });
});
cart의 조회기능은 "/goods/cart"이니
api 요청을 보낼 때
localhost:3000/api/goods/cart로 보냈는데,
위쪽에 존재했던 goodsId값으로 조회하는 "/goods/:goodsId" 부분에서 cart가 id값으로 들어간 채로 해당 부분을 반환하고 있던 것이었다...
강의에서는 숫자로만 예시를 들어서 미처 생각하지 못했던 부분이었는데 해당 부분을 주석처리하고 다시 테스트를 해보니 정상적으로 작동하였다.
"/:값"의 구조로 params를 받아줄 때, 다른 동일한 api요청과 겹치지 않도록 신경써야할 것 같다.
전부터 루아스크립트를 사용한 게임 툴에서 작업을 이어오고 있었다.
코딩을 배운게 아니라 그냥 독학으로 필요한 기능들만 찾아다가 구현하던 와중에 node.js 공부를 시작하게 되며 함수에 내장된 기본 메서드들이나 이런 저런것들을 배우게 되니 기존 작업물에 사용해보고 싶어졌다...
간략하게 설명을 남기면
서버에서 작동하는 damageCallback 함수에서 target, damage 등의 간략한 정보만 클라이언트로 보내주는 damageCallback함수이다. 여기서 damage값을 return 해줌으로써 유저의 화면에 표기되는 damage 이펙트 값이 결정된다.
여기서 기존에 숫자로만 출력되던 damage값을 한글로, 혹은 한글단위로 출력해주고자 했다.
Client.damageCallback = function (a,damage)
local strDamage = tostring(damage)
return changeDamageA(strDamage)
end
target 인자는 사용하지 않으므로 a로 받아만두었고,
받아온 number 타입의 damage을 문자열로 변환시켜주었다. 그리고 changeDamageA()함수에 집어넣었는데 해당 함수 내용은 다음과 같다.
function changeDamageA(damage)
local l = #damage
local retxt = damage
if l <= 4 then
return retxt
elseif l <= 8 then
retxt = string.sub(damage,1,l-4).. "만" .. string.sub(damage,l-3,8)
return retxt
elseif l <= 12 then
retxt = string.sub(damage,1,l-8) .. "억" .. string.sub(damage,l-7,l-4).. "만".. string.sub(damage,l-3,12)
return retxt
else
retxt = string.sub(damage,1,l-12) .. "조" .. string.sub(damage,l-11,l-8).. "억" .. string.sub(damage,l-7,l-4) .. "만" .. string.sub(damage,l-3,16)
return retxt
end
end
자릿수 4번마다 만, 억, 조의 단위가 붙으면 되기에
자릿수를 판별해주고 해당 위치에 한글로 단위 텍스트를 붙여주었다.
lua스크립트를 검색해본 결과 replace가 존재한다고 나와있었으나, 비교적 최근버전에만 추가된 상황이었고
내가 사용중인 툴의 lua 버전에서는 replace가 존재하지 않았다. print(string.replace)로 확인해보았으나 할당된 주소값이 아닌 nil을 반환하였다.
그래서 차선책으로 string.sub()을 통해서 4자리수마다 끊어주었고 lua에서 사용되는 .. .. 기호를 통해 서로 이어 붙였다.
예시)
자릿수가 8자리 이하일 경우 1234만5678이 출력된다.
replace사용이 불가하다는걸 확인하고 조금 많이 당황했지만 다른방식으로 해결이 가능했다.
이게 1번 변환 케이스이고 2번 변환 케이스도 만들어보았다.
2번 케이스의 경우 1을 일, 3을 삼, 6을 육 ...
이런식으로 정말 숫자를 읽는 발음대로만 표기해보았다.
(여러 게임들의 비슷한 대미지 스킨이 떠올라서 만들어보았다.)
local number = {'영','일', '이', '삼', '사', '오', '육', '칠', '팔', '구'}
function changeDamageB(damage)
for i = 1, 10, 1 do
damage = string.gsub(damage,i-1,number[i])
end
return damage
end
우선 changeDamageB()함수 내에서 number테이블을 선언해주면 damageCallback함수가 실행 될 때 마다(대미지가 들어갈 때 마다) number테이블 역시 반복해서 선언, 할당되기 때문에 함수의 바깥으로 위치를 시켰다.
그리고 for문으로 돌렸는데 내부 구조를 보면 string.gsub()이라는걸 사용했는데, 첫번째 인자로 들어간 문자열에서 두번째 인자로 들어간 문자열을 3번째 인자로 들어간 문자열로 대체해준다.
i를 1부터 선언해주고 사잇값을 i-1로 한 이유는 number 테이블을 이용하여 대체할 문자열을 지정해주는데 lua에서는 javaScript와 다르게 인덱스 번호가 0이 아닌 1부터 시작하기 때문이었다.
여담인데
처음에는 문자열 번호에 직접 접근하는 방식으로 구현을 하려고 했다.
local str = "abcd"
print(str[1])
하지만.. 루아에서는 자바스크립트처럼 이러한 접근 자체가 불가능했다.
기존 개발을 할 때는, replace나 sub나 이러한 유용한 기능들이 존재하는줄도 몰랐다가, node를 공부하게 되면서 알게되었는데, 이를 실제로 내가 하던 작업에 쓰게 되니까 뿌듯하기도 하고 기분이 좋았다!
(해당 대미지 변환 스크립트는 관련 툴 카페에 게시해서 공유도 했다.. 뿌듯)