오늘의 목표

더보기

✔️ 팀 프로젝트

 


⏱️ 오늘의 일정

9:00 ~ 21:00 - 팀 프로젝트 


📜 팀 프로젝트

9:00 ~ 21:00 - 팀 프로젝트 

 

팀 프로젝트를 진행했다. 

테스트용으로 만든 회원가입과 로그인 부분을 내가 생각하는 로직으로 바꿧다.

 

우선 로그인 전용으로 인증을 검사하는 미들웨어를 만들었다.

export default async function (req, res, next) {
    const { authorization } = req.headers;
    const { id } = req.body;

    do {
        // AccessToken이 없음
        if (authorization === undefined || authorization.length == 0) {
            req.authLoginState = process.env.LOGIN_AUTH_FAIL_AUTHORIZATION_NOT_FOUND;
        }
        else {
            // AccessToken이 있음
            const [c2sTokenType, c2sAccessToken] = authorization.split(' ');
            if (c2sTokenType !== process.env.TOKEN_TYPE_CHECK) {
                req.authLoginState = process.env.LOGIN_AUTH_FAIL_TOKEN_TYPE_NOT_MATCH;
            }        

            const decodedToken = jwt.decode(c2sAccessToken);            
            if(decodedToken.id !== id)
            {
                req.authLoginState = process.env.LOGIN_AUTH_FAIL_DIFFERENT_ACCESS_TOKEN;
                break;
            }

            // 토큰 검증
            const verifyToken = ValidateToken(c2sAccessToken, process.env.ACCESS_TOKEN_SECRET_KEY);                        
            if (!verifyToken) {
                req.authLoginState = process.env.LOGIN_AUTH_FAIL_TOKEN_EXPIRED;
                break;
            }            

            req.authLoginState = process.env.LOGIN_AUTH_SUCCESS;
        }
    } while (0);

    next();
}

 

위 코드에서는 로그인 시도한 유저가 AccessToken을 가지고 있는지 확인한다.

AccessToken이 없거나 빈 내용을 입력하면 req.authLoginState에 LOGIN_AUTH_FAIL_AUTHORIZATION_NOT_FOUND를 저장한다.

 

AccessToken이 있는데 TokenType이 다를 경우에 LOGIN_AUTH_FAIL_TOKEN_TYPE_NOT_MATCH를 저장한다.

AccessToken이 있는데 다른 유저의 AccessToken을 가지고 왔을 경우에는 LOGIN_AUTH_FAIL_DIFFERENT_ACCESS_TOEKN을 저장한다.

 

AccessToken이 있는데 만료됐거나, 값이 다를 경우네는 LOGIN_AUTH_FAIL_TOKEN_EXPIRED를 저장한다.

 

그 외 경우에는 인증 성공으로 판단해서 LOGIN_AUTH_SUCCESS를 저장한다.

 

이후 메인 Login Router로 진행한다.

 

// 로그인

usersRouter.post('/Sign-In', loginAuthMiddleware, async (req, res, next) => {
    // 아이디, 비밀번호 가져오기
    const { id, password } = req.body;

    // userDB에 아이디가 있는지 확인
    const user = await prisma.users.findFirst({
        where: {
            id: id
        }
    });

    // 아이디 검사
    if (!user) {
        return res.status(404).json({ message: `${id}은 존재하지 않는 아이디 입니다.` });
    }

    // 비밀번호 검사
    if (!(await bcrypt.compare(password, user.password))) {
        return res.status(404).json({ message: `비밀번호가 일치하지 않습니다.` });
    }

    // RefreshTokens 테이블에 RefreshToken이 있는지 확인
    const refreshTokenExist = await prisma.refreshTokens.findFirst({
        where: {
            id: id
        }
    });

    switch (req.authLoginState) {
        case process.env.LOGIN_AUTH_FAIL_AUTHORIZATION_NOT_FOUND:
            // AccessToken이 없음
            FirstLogin(id, refreshTokenExist, res);
            break;
        case process.env.LOGIN_AUTH_FAIL_TOKEN_TYPE_NOT_MATCH:
            // AccessToken을 가지고 있지만 토큰 타입이 일치하지 않음
            return res.status(404).json({ message: '토큰 타입이 일치하지 않습니다.' });
        case process.env.LOGIN_AUTH_FAIL_TOKEN_EXPIRED:
            // AccessToken이 만료됨
            AccessTokenExpired(id, refreshTokenExist, res);
            break;
        case process.env.LOGIN_AUTH_FAIL_DIFFERENT_ACCESS_TOKEN:
            DifferentAccessToken(refreshTokenExist, res);
            break;
        case process.env.LOGIN_AUTH_SUCCESS:
            // 로그인 성공
            return res.status(200).json({ message: `${id}로 로그인에 성공했습니다.` });
    }
})

 

로그인에서는 기본적으로 아이디와 비밀번호를 검사하고,

RefreshToken Table에 RefreshToken이 있는지 찾은 후,

로그인 인증에서 저장한 authLoginState값에 따라 다른 로직으로 처리한다.

 

//------------------------------------------------------
// 처음 로그인
//------------------------------------------------------
async function FirstLogin(id, refreshTokenDBData, res) {
    if (refreshTokenDBData === null) {
        // RefreshToken이 Table에 없음
        // 처음 로그인 대상으로 해석
        // JWT로 AccessToken 생성
        const s2cAccessToken = CreateAccessToken(id);
        // JWT로 RefreshToken 생성
        const s2cRefreshToken = CreateRefreshToken(id);

        // 응답 헤더에 accessToken 기록
        res.header("authorization", s2cAccessToken);

        const nowTime = MakeTime(false);
        const expiredAtTokenTime = MakeTime(false, parseInt(process.env.REFRESH_TOKEN_EXPIRE_TIME_DB));

        // RefreshToken DB에 저장
        const refreshToken = await prisma.refreshTokens.create({
            data: {
                id: id,
                token: s2cRefreshToken,
                createdAt: nowTime,
                expiredAt: expiredAtTokenTime
            }
        });

        return res.status(200).json({ message: `${id}로 로그인에 성공했습니다.` });
    }
    else {        
        const s2cAccessToken = CreateAccessToken(id);
        res.header("authorization", s2cAccessToken);

        return res.status(200).json({ message: `${id}로 로그인에 성공했습니다.` });
    }
}

 

FirstLogin 에서는 로그인 시도한 대상이 처음으로 로그인한 대상인지를 판단한다.

만약 RefreshToken Table에서 RefreshToken을 찾지 못하면, 해당 유저를 처음 로그인한 대상으로 해석하고,

AccessToken과 RefreshToken을 발행하고, Refresh Token을 Table에 저장한다.

 

RefreshToken을 이미 가지고 있는 대상이면 AccessToken을 재발행해준다.

 

async function AccessTokenExpired(id, refreshTokenDBData, res) {
    if (refreshTokenDBData !== null) {
        // AccessToken을 가지고 있고, AccessToken이 만료됨
        // refreshToken을 DB에 가지고 있음

        // AccessToken을 재발급
        // RefreshToken이 만료되었는지 확인
        const refreshTokenCheck = ValidateToken(refreshTokenDBData, process.env.REFRESH_TOKEN_SECRET_KEY);
        if (!refreshTokenCheck) {
            // RefreshDB createdAt, expiredAt 컬럼 사용 용도 : 
            // JWT 없이 Refresh Token의 Expire 시간을 제어할 수 있는 용도로 사용
            // 예) JWT로 3시간 후 만료로 발급했으나 모종의 이유로 1시간 후 만료로 바꿔야하는 경우,
            //     JWT는 수정을 할 수 없기 때문에 DB에 있는 시간값으로 판정한다.

            // 현재 시간과 db에 저장되어 있는 시간 차를 구함
            const dbTimeDifferenceSecond = TimeDifference(refreshTokenDBData.expiredAt, "second");

            // 상수값으로 설정해둔 Refresh Token 만료 시간을 지나면 
            if (dbTimeDifferenceSecond >= parseInt(process.env.REFRESH_TOKEN_EXPIRE_DB_CHECK_TIME)) {
                // RefreshToken을 재발급
                const s2cRefreshToken = CreateRefreshToken(id);

                // 현재 시간을 구함
                const nowTime = MakeTime(false);
                // Refresh Token 만료 시간을 구함
                const expiredTime = MakeTime(false, parseInt(process.env.REFRESH_TOKEN_EXPIRE_TIME_DB));

                // Refresh Token을 업데이트
                await prisma.refreshTokens.update({
                    where: { id: id },
                    data: {
                        token: s2cRefreshToken,
                        createdAt: nowTime,
                        expiredAt: expiredTime
                    }
                });
            }
        }

        const s2cAccessToken = CreateAccessToken(id);
        res.header("authorization", s2cAccessToken);

        return res.status(200).json({ message: `AccessToken이 재발급 되었습니다. ${id}로 로그인에 성공했습니다. ` });
    }
    else {
        // AccessToken을 가지고 있고, AccessToken이 만료됨
        // refreshToken을 DB에 가지고 있지 않음
        // 불량 유저로 판정 ( 서버에 문제가 생겨 refreshToken을 기록 못할경우가 있어서 불량 유저로 판정하기엔 무리)      
        // return res.status(404).json({ message: `비정상 로그인 시도` });

        return res.status(404).json({ message: `refreshToken DB Empty` });
    }
}

 

AccessTokenExpired에서는 AccessToken을 재발행 해준다.

만약 RefreshToken을 DB에 가지고 있지 않을 경우에는 불량유저로 판단해 로그인 시켜주지 않는다.

( 이 부분은 실제 이런경우가 있는지 보고 싶어서 따로 처리한 것이고, 원래는 서버에 문제가 생겨 RefreshToken을 DB에 기록하지 못할 경우도 생길 수 있기 때문에 AccessToken을 재발행 해줘야한다고 튜터님에게 답을 얻었다. )

 

RefreshToken을 DB에 가지고 있으면, RefreshToken이 만료됐는지 JWT로 먼저 확인해주고,

추가로 DB에 있는 시간값을 기준으로 만료됐는지 확인 한 후,

( DB에 있는 시간값을 기준으로 만료됐는지 추가로 확인하는 이유는 다음과 같다. JWT로 발행을하고 나서 모종의 이유로 만료시간을 수정을 해야할 경우, JWT의 만료시간을 수정할 수 없어서 DB값을 기준으로 최종적으로 판단해준다. )

만료됐으면 RefreshToken을 재발행한다.

 

🌙 하루를 마치며

오늘 생각했던 회원가입과 로그인 부분을 구현할 수 있어서 기분이 좋았다.

로그인 부분에서 불량유저를 판단하는 부분이 애매해 튜터님에게 가서 물어보니,

다른 관점으로 대답을 해주셔서 판단에 많은 도움이 되었다.

 

주말에는 추가로 맡은 랭킹 부분을 완료해 월요일에 테스트 할 수 있도록 해야겠다.

오늘의 목표

더보기

✔️ 프로그래머스 코테 문제 풀기

✔️ 팀 프로젝트 ( 회원가입, 로그인 )


⏱️ 오늘의 일정

9:00 ~ 10:00 - 프로그래머스 코테 문제 풀기
10:00 ~ 11:00 - 스탠다드반 CS 강의

11:00 ~ 21:00 - 팀 프로젝트

19:00 ~ 20:00 - 챌린지반 수업 

 


📜 프로그래머스 코테 문제 풀기

9:00 ~ 10:00 - 프로그래머스 코테 문제 풀기

 

대충만든 자판

 

https://github.com/YamSaeng/AlgorithmCodingTest/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/160586.%E2%80%85%EB%8C%80%EC%B6%A9%E2%80%85%EB%A7%8C%EB%93%A0%E2%80%85%EC%9E%90%ED%8C%90

 

AlgorithmCodingTest/프로그래머스/1/160586. 대충 만든 자판 at main · YamSaeng/AlgorithmCodingTest

This is an auto push repository for Baekjoon Online Judge created with [BaekjoonHub](https://github.com/BaekjoonHub/BaekjoonHub). - YamSaeng/AlgorithmCodingTest

github.com

 

function solution(keymap, targets) {
    var answer = [];    
    for (let i = 0; i < targets.length; i++) {                
        answer.push(0);
        for (let j = 0; j < targets[i].length; j++) {
            const targetChar = targets[i][j];
            let minBtnClick = 1000;            
            let findChar = false;
            for (let k = 0; k < keymap.length; k++) {
                for (let l = 0; l < keymap[k].length; l++) {
                    if (keymap[k][l] == targetChar) {
                        if (l < minBtnClick) {
                            minBtnClick = l + 1;
                            findChar = true;
                        }
                    }
                }
            }
            if (findChar == false) {
                answer[i] = -1;
                break;
            }
            
            answer[i] += minBtnClick;
        }        
    }    
    return answer;
}

 

문제 설명에 있는 대로 반복문을 사용해 구현했다.

반복문이 많아서 시간초과가 날까 싶었는데, 초과하는 예시는 없었다.

targets 의 개수 만큼 answer에 0을 넣고 해당 값을 증가시켜주는 방식으로 구현했다.

 

 

📜 스탠다드반 CS 강의

9:00 ~ 10:00 - 스탠다드반 CS 강의

 

IP 주소와 클래스풀 주소 체계에 대한 내용과 서브넷 마스크를 배웠다.

스탠다드반 수업내용은 따로 정리해 포스팅하고 링크를 남겨야겠다.

 

 

📜 팀 프로젝트

11:00 ~ 21:00 - 팀 프로젝트

 

본격적인 팀 프로젝트 구현 첫 날을 맞았다.

우선 기초적인 회원가입과 로그인을 구현해 Push 했다.

 

회원가입 api

// 회원가입
usersRouter.post('/Sign-Up', async (req, res, next) => {
    const { name, nickname, id, password, confirmPassword } = req.body;
    const isExistUser = await prisma.users.findFirst({
        where: {
            id: id
        }
    });

    if (isExistUser) {
        return res.status(404).json({ message: `이미 존재하는 아이디입니다.` });
    }

    if (confirmPassword === undefined) {
        return res.status(404).json({ message: `비밀번호 확인을 입력하세요` });
    }

    if (password !== confirmPassword) {
        return res.status(404).json({ message: `비밀번호와 비밀번호 확인이 일치하지 않습니다.` });
    }

    const hashedPassword = await bcrypt.hash(password, 10);

    // 유저 생성해서 Users table에 저장
    const user = await prisma.users.create({
        data: {
            name: name,
            nickname: nickname,
            id: id,
            password: hashedPassword
        }
    });

    // 랭킹 생성해서 Ranking table에 저장
    const rank = await prisma.ranking.create({
        data: {
            userId: user.userId
        }
    });

    // 스쿼드 생성해서 Squad table에 저장
    const squad = await prisma.squad.create({
        data: {
            userId: user.userId
        }
    });

    return res
        .status(201)
        .json({ message: `${id}로 회원가입이 완료되었습니다.` });
});

 

회원가입은 위 코드에서 추가할 부분이 없어보이기는 한다.

아마 닉네임 중복 점검 정도 추가하지 않을까 싶다.

 

 

 

로그인 api

usersRouter.post('/Sign-In', async (req, res, next) => {
    // 아이디, 비밀번호 가져오기
    const { id, password } = req.body;
    // AccessToken이 있는지 확인
    const { authorization } = req.headers;

    // userDB에 아이디가 있는지 확인
    const user = await prisma.users.findFirst({
        where: {
            id: id
        }
    });

    // 아이디 검사
    if (!user) {
        return res.status(404).json({ message: `${id}은 존재하지 않는 아이디 입니다.` });
    }

    // 비밀번호 검사
    if (!(await bcrypt.compare(password, user.password))) {
        return res.status(404).json({ message: `비밀번호가 일치하지 않습니다.` });
    }

    // JWT로 AccessToken 생성
    const s2cAccessToken = CreateAccessToken(id);
    // JWT로 RefreshToken 생성
    const s2cRefreshToken = CreateRefreshToken(id);   

    // 응답 헤더에 accessToken 기록
    res.header("authorization", s2cAccessToken);

    return res.status(200).json({ message: `${id}로 로그인에 성공했습니다.` });
})

 

로그인은 기본적으로 jwt로 accesstoken을 발급하고, 헤더에 저장해 발급해주는 방법을 우선 사용했다.

테스트를 해야하기 때문에 인증은 간단하게!

 

로그인은 RefreshToken을 활용해 로직을 구현하려고 한다.RefreshToken을 DB에 저장해 관리한다.AccessToken이 만료되면, RefreshToken으로 AccessToken의 재발행 여부를 판단한다.

 

 

 

📜 챌린지반 수업

19:00 ~ 20:00 - 챌린지반 수업 

 

오늘 챌린지반 수업에서는 DB Query 중 집계함수와 그룹핑에 대한 내용을 배웠다.

추가로 숙제를 내줬는데 프로그래머스 SQL 문제 4개를 풀어오는 숙제다.

 

랜덤하게 챌린지반 수강생 중 3명을 골라 발표를 시킨다고 공지를 받았다.

SQL 문제를 풀고, TIL에 기록해야겠다.

오늘의 목표

더보기

✔️ 스탠다드반 수업

✔️ 개인 과제 마무리

✔️ 데이터 베이스개념, 인증, 인가 특강

✔️ 챌린지반 수업


⏱️ 오늘의 일정

10:00 - 11:00 - 스탠다드 반 수업
11:00 ~ 12:00 - 개인 과제 마무리
 

12:00 ~ 13:00 - 개인 과제 다듬기

13:00 ~ 14:00 - 점심시간

14:00 ~ 16:00 - 데이터 베이스개념, 인증, 인가 특강

16:00 ~ 18:00 - 개인 과제 다듬기

18:00 ~ 19:00 - 저녁시간

19:00 ~ 20:00 - 챌린지반 수업20:00 ~ 21:00 - 팀원 코드 리뷰


📜 스탠다드 반 수업

10:00 - 11:00 - 스탠다드 반 수업

 

오늘 수업은 발제자분들의 발표로만 이루어졌다.

OSI 7계층 중 3계층인 네트워크 계층에 대한 주제로 발표가 시작되었다.

 

 

📜 개인 과제 마무리

11:00 - 12:00 - 개인과제 마무리

 

 

길고 길었던 개인과제를 마무리했다.

개인과제를 마무리하고 미처 구현하지 못했던 기능들도 구현해 추가로 제출했다.

 

아이템 구입할때 발견하지 못했던 부분을 발견해서 버그를 수정했다.

 

 

 

 // 캐릭터의 인벤토리를 찾는다.
 const chracterInventory = await prisma.inventory.findFirst({
     where: {
         characterId: character.characterId
     }
 });
 
 // 인벤토리에서 비어있는 공간을 찾는다.
 const emptyInventoryItem = await prisma.inventoryItem.findFirst({
     where: {
         inventoryId: chracterInventory.inventoryId,
         itemId : 0
     }
 });

 // 비어있는 공간에 아이템을 넣는다.
 await prisma.inventoryItem.update({
     where: {
         inventoryItemIndex: emptyInventoryItem.inventoryItemIndex,
         inventoryId : chracterInventory.inventoryId
     },
     data: {
         itemId: searchItem.itemId,
         inventoryItemCount: count
     }
 })

 

위 코드는 아이템을 구입하고 인벤토리에 넣을 때, 인벤토리에서 빈 공간을 찾은 후 해당 공간에 아이템을 넣는 코드다.

비어있는 공간에 아이템을 넣는 부분의 로직은 다음과 같다.

 

인벤토리 아이템을 업데이트 하는데, 인벤토리 아이템의 위치값이 위에서 찾은 비어있는 공간의 아이템의 위치값과 같고,해당 인벤토리 아이템이 캐릭터가 소유하고 있는 인벤토리일 경우,해당 인벤토리 아이템의 아이템 코드와 개수를 업데이트 한다.

 

이처럼 구성을하고 실행을 하니 에러가 났다.

 

 

에러 내용을 살펴보니 inventoryitemId를 활용하라는 의미의 버그였다.

생각해봐도 왜 굳이 저거를 써야할까..?

로우 쿼리 자체가 문제가 되는가 싶어서 MySQL Workbench에서 위 로직으로 쿼리를 짜서 실행해보니 문제 없이 실행됐다.

 

Prisma 자체의 문제인가 싶어서 튜터님에게 찾아가 문의를 하니, Prisma에서는 PK로 설정한 값을 활용해 where문을 작성해야한다는 말을 들었다.

 // 비어있는 공간을 찾는다.
 const emptyInventoryItem = await prisma.inventoryItem.findFirst({
     where: {
         inventoryId: chracterInventory.inventoryId,
         itemId: 0
     }
 });

// 비어있는 공간에 아이템을 넣는다.
await prisma.inventoryItem.update({
    where: {
        inventoryItemId: emptyInventoryItem.inventoryItemId                    
    },
    data: {
        itemId: searchItem.itemId,
        inventoryItemCount: count
    }
});

 

그래서 위처럼 수정을 했다.

 

인벤토리 아이템을 업데이트 하는데,

인벤토리 아이템의 ID 값이 비어있는 아이템의 ID 값이 같은 대상을 찾고,

해당 인벤토리 아이템의 아이템 코드와 개수를 업데이트한다.

 

이처럼 구성을하고 실행을 하니 깔끔하게 업데이트가 되었다.

위처럼 구성하는 것이 기본적인 방법이라고 튜터님에게서 조언을 받았다.

기본 ID 값을 활용해 업데이트 하는 방식으로 SQL 쿼리를 만들어야겠다.

 

📜 데이터베이스 개념, 인증 인가 특강

14:00 ~ 16:00 - 데이터 베이스개념, 인증, 인가 특강

 

앞서 노드 강의에서 배운 데이터베이스의 개념과 인증, 인가에 정리 차원에서 특강 수업이 있어서 들었다.

 

📜 챌린지반 수업

19:00 - 20:00 - 챌린지 반 수업

 

매주 화 목 진행하는 챌린지반 수업에 참여했다.

이번 강의에서는 RDBMS에 대한 개념을 주제로 해서 수업이 이루어졌다. 

중간 중간 쿼리에 대한 문제를 내주고 푸는 짧은 문제 풀이 시간도 있었다.

 

📜 팀원 코드 리뷰

20:00 - 21:00 - 팀원 코드 리뷰

 

20시 부터 팀원 코드 리뷰 시간이 있어서 참여했다.

다른 분들의 코드를 보면서 의아한 부분은 질문하고, 도움이 될만한 부분을 발견해 많은 도움이 되었다.

 

https://dbdiagram.io/home

 

dbdiagram.io - Database Relationship Diagrams Design Tool

 

dbdiagram.io

 

무료로 DB 다이어그램을 구성할 수 있는 사이트를 공유 받아서 앞으로 자주 이용해야겠다.

 

🌙 하루를 마치며

오늘로 개인과제가 마무리 되었다.

9월 13일 오전 10시에 팀 과제 발제가 있다고 공지를 받았다.

어떤 문제가 나올지 기대되고, 팀원 코드 리뷰를 하면서 팀 과제 분배를 어떻게 할지도 상의했다.

우선 지금 생각으로는 개인과제를 하며 발견한 router 단위로 분배를 하는법

아니면 어떤 큼직한 로직 담당으로 분배를 하는법 2가지 정도를 생각하고 의견을 나눴다.

 

최종으로는 팀 과제 발제를 듣고나서 적절하게 분배해 수행하는 것으로 이야기를 마루리햇다.

오늘의 목표

더보기

✔️ 개인 과제 구현 


⏱️ 오늘의 일정

10:00 ~ 11:00 - 스탠다드반 CS 강의

11:00 ~ 19:00 - 개인과제 구현

19:00 ~ 20:30 - 챌린지반 수업


📜 스탠다드 반 CS 강의

10:00 ~ 11:00 - 스탠다드 반 CS 강의

 

매주 화목에 진행하는 스탠다드반 수업 강의가 있어서 참여했다.

오늘 내용은 이더넷, 허브, 스위치, Mac 주소에 관해 배웠다.

 

📜 개인과제 구현

11:00 ~ 19:00 개인과제 구현

 

git Commit 목록

 

오늘은 속도가 좀 붙어서 로그인 기능과 캐릭터 생성, 캐릭터 삭제, 캐릭터 조회, 아이템 생성 까지 구현했다.

 

로그인 기능에서 고민이 있던 부분을 포스팅하고자 한다.

처음에는 로그인 할때 아이디와 비밀번호가 맞으면 AccessToken을 생성해 반환해주는 걸로 우선 구현을 했다.

 

이렇게 구현하다가는 만약 재로그인한 대상이 해당 유저가 아닐 경우에는? 어떻게 해야할까? 라고 생각이 들어서 재로그인할때는 AccessToken을 살펴보기로 하고 구현했다.

 

내가 생각한 로그인 방식은 다음과 같다.

 1. 로그인 요청한 대상이 AccessToken을 가지고 오지 않았으면

  1.1 처음 발급 하는것이므로 AccessToken과 RefreshToken을 발급하고 로그인 성공 처리한다.

 2. 로그인 요청한 대상이 AccessToken을 가지고 오면

  2.1 해당 AccessToken이 유효한지 판단한다.

   2.1.1 AccessToken이 유효하면 로그인 성공 처리한다.

   2.1.2 AccessToken이 유효하지 않으면, RefreshToken을 가지고 있는지 확인한다.

    2.1.2.1 RefreshToken을 가지고 있으면, RefreshToken이 유효한지 판단한다.

     2.1.2.1.1 RefreshToken이 유효하면, AccessToken을 재발급하고 로그인 성공 처리한다.

     2.1.2.1.2 RefreshToken이 유효하지 않으면, RefreshToken과 AccessToken을 재발급하고 로그인 성공 처리한다.

    2.1.2.2 RefreshToken을 가지고 있지않으면, RefreshToken을 발급하고 AccessToken을 재발급하고 로그인 성공처리

 

위처럼 생각하고 구현해보니 어떻게해도 무조건 로그인은 성공처리가 되는것이 아닌가?

 

다시한번 생각하니 로그인 처리에서는 AccessToken과 RefreshToken을 발급 하는것이 목적인것이고 ( 아이디와 비밀번호만 맞으면 ), 클라이언트에서는 AccessToken을 가지고 로그인을 제외한 다른 작업을 할때, '검증' 하는 것을 목적으로 삼으면 되는것이니까.

 

로그인 처리에서는 앞서 언급했듯이 아이디와 비밀번호만 맞으면 로그인 성공처리로 해주면 되겠구나라는 생각이 들어서 그대로 진행했다.

 

로그인 처리를 제외하고는 다른 캐릭터 생성, 캐릭터 삭제 등 인증이 필요한 곳에는 AccessToken으로 검증을 해 해당 값이 유효하지 않으면 바로 에러를 반환하도록 구현했다.

 

위처럼 생각한 것이 맞는지는 확신할 수 없다.

 

https://velog.io/@chuu1019/Access-Token%EA%B3%BC-Refresh-Token%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EA%B3%A0-%EC%99%9C-%ED%95%84%EC%9A%94%ED%95%A0%EA%B9%8C

 

🧐 Access Token과 Refresh Token이란 무엇이고 왜 필요할까?

JWT 토큰은 유저의 신원이나 권한을 결정하는 정보를 담고 있는 데이터 조각이다. JWT 토큰은 비밀키로 암호화되어 있기에 비교적 안전하다. 그런데 탈취 당했을 때가 문제다!! 어떻게 위험을 최

velog.io

위 글을 보면 맞는거 같기도 하고.. 애매..

 

내일 과제를 완료하면 따로 튜터님한테 가서 물어보긴 해야겠다.

 

로그인

// 로그인
router.post('/sign-in', async (req, res, next) => {
    const { id, password } = req.body;
    const user = await prisma.users.findFirst({
        where: { id }
    });

    // 아이디와 비밀번호 검사
    if (!user) {
        return res.status(401).json({ message: `${id}은 존재하지 않는 아이디 입니다.` });
    }
    else if (!(await bcrypt.compare(password, user.password))) {
        return res.status(401).json({ message: `비밀번호가 일치하지 않습니다.` });
    }

    // 재로그인할때 필요한 accessToken을 가져옴
    const c2sAccessToken = req.cookies.accessToken;    

    let s2cAccessToken = 0;
    let s2cRefreshToken = 0;

    // accessToken 발급 여부 판단
    let newAccessToken = false;

    if (!c2sAccessToken) // 액세스 토큰이 없음
    {
        // 액세스 토큰 새 발행
        newAccessToken = true;
    }
    else // 액세스 토큰이 있음
    {        
        // 액세스 토큰 유효한지 확인
        // 유효하면 로그인 성공
        const [tokenType, token] = c2sAccessToken.split(' ');

        // tokenType이 맞는지 확인
        if (tokenType !== process.env.TOKEN_TYPE_CHECK) {
            return res.status(404).send('not found');
        }

        // 다른 유저의 AccessToken을 가지고 왔을 경우
        // 현재 로그인한 유저를 대상으로 AccessToken 재발행
        const myToken = CreateAccessToken(id);
        if (myToken !== c2sAccessToken)
        {
            newAccessToken = true;
        }
        else
        {
            // 나의 AccessToken을 가지고 오면 유효한지 확인
            const payload = ValidateToken(token, process.env.ACCESS_TOKEN_SECRET_KEY);
            if (!payload) // 액세스 토큰이 유효하지 않음
            {
                // 액세스 토큰 새 발행
                newAccessToken = true;
            }
        }        
    }

    // 액세스 토큰 새로 발급
    if (newAccessToken) {
        // DB에서 리프레시 토큰을 읽어옴
        const dbRefreshToken = await prisma.refreshTokens.findFirst({
            where: { userId: id },
            select: {
                token: true
            }
        });        

        // DB에 리프레시 토큰이 없음
        if (dbRefreshToken == null)
        {
            // 액세스 토큰과 리프레시 토큰을 새로 발급
            s2cAccessToken = CreateAccessToken(id);
            s2cRefreshToken = CreateRefreshToken(id);

            // 리프레시 토큰을 DB에 저장
            const newDBRefreshToken = await prisma.refreshTokens.create({
                data: {
                    userId: id,
                    token: s2cRefreshToken
                }
            });   

            // 쿠키 전달
            res.cookie('accessToken', s2cAccessToken);
            res.cookie('refreshToken', s2cRefreshToken);
        }
        else 
        {
            // DB에 리프레시 토큰이 있음

            const [tokenType, token] = dbRefreshToken.token.split(' ');

            // tokenType이 맞는지 확인
            if (tokenType !== process.env.TOKEN_TYPE_CHECK) {
                return res.status(404).send('not found');
            }

            // 리프레시 토큰이 유효한지 확인
            const dbRefreshTokenCheck = ValidateToken(token, process.env.REFRESH_TOKEN_SECRET_KEY);
            if (dbRefreshTokenCheck) // 리프레티 토큰이 유효함
            {
                // 액세스 토큰 발급
                s2cAccessToken = CreateAccessToken(id);                

                // 액세스 토큰 전달
                res.cookie('accessToken', s2cAccessToken);
            }
            else // 리프레티 토큰이 유효하지 않음
            {
                // 액세스 토큰과 리프레시 토큰을 새로 발급
                s2cAccessToken = CreateAccessToken(id);
                s2cRefreshToken = CreateRefreshToken(id);

                // 리프레시 토큰을 DB에 업데이트
                const newDBRefreshToken = await prisma.refreshTokens.update({
                    where: { userId: id },
                    data: {                        
                        token: s2cRefreshToken
                    }
                }); 

                // 쿠키 전달
                res.cookie('accessToken', s2cAccessToken);
                res.cookie('refreshToken', s2cRefreshToken);
            }
        }
    }

    return res
        .status(200)
        .json({ message: `${id}로 로그인 성공` });
});

 

 

캐릭터 생성, 캐릭터 삭제, 캐릭터 조회에서는 authMiddleware를 통해 검증하도록 구현했다.

export default async function (req, res, next) {
    const c2sAccessToken = req.cookies.accessToken;
    if (c2sAccessToken === undefined) {
        return res.status(404).json({ message: 'not found' });
    }

    const [tokenType, accessToken] = c2sAccessToken.split(' ');
    if (tokenType !== process.env.TOKEN_TYPE_CHECK) {
        return res.status(401).json({ message: '토큰 타입이 일치하지 않습니다.' });
    }

    // 토큰 검증
    const decodedToken = ValidateToken(accessToken, process.env.ACCESS_TOKEN_SECRET_KEY);
    if (!decodedToken) {
        return res.status(401).json({ message: '토큰이 만료되었습니다. 로그인을 다시 해주세요' })
    }

    const id = decodedToken.id;

    const user = await prisma.users.findFirst({
        where: {
            id : id
        }
    });

    if (!user) {
        return res.status(401).json({message:`${id} 사용자를 찾을수 없습니다.`})
    }

    req.user = user;

    next();    
}

 

로그인한 유저가 AccessToken을 들고오지 않았을 경우, TokenType이 일치하지 않을 경우,

그리고 Token이 만료할 경우 에러를 반환하도록했다.

오늘의 목표

더보기

✔️ 개인과제 발제

✔️ Node.js 강의

 


⏱️ 오늘의 일정

09:00 ~ 10:00 - 병원

10:00 ~ 11:00 - 개인과제 발제
11:00 ~ 11:30 - 팀 노션 작성

11:30 ~ 21:00 - Node.js 강의 듣기 


📜 병원

09:00 ~ 10:00 - 병원

 

엄마 수술 일정으로 인해 병원에 다녀왔다.

 

📜 개인과제 발제

10:00 ~ 11:00 - 개인과제 발제

 

줌으로 개인과제 발제가 있어서 참여했다.

이번에는 아이템 시뮬레이터를 할 수 있는 웹서버를 만드는 과제가 출제되었다.

아직 Node.js 강의를 다 수강하지 않아서 숙련까지 모두 토요일까지 듣고 과제를 시작할 수 있도록 해야겠다.

 

📜 팀 노션 작성

11:00 ~ 11:30 - 팀 노션 작성

 

3주차 새로운 팀에 배정되었다.

팀원분들과 얘기하고 노션에 간단한 정보를 입력했다.

다음주 월, 수에 개인과제를 팀원들끼리 공유해서 코드 리뷰 해보는 시간을 가지기로 했다.

 

📜 Node.js 강의 듣기 

11:30 ~ 21:00 - Node.js 강의 듣기 

 

2024.09.06 - [데이터베이스] - [DATABASE] Raw Query

 

[DATABASE] Raw Query

Raw Query는 SQL을 Node.js에서 사용해 데이터베이스에 쿼리를 요청 할 수 있는 방법이다.SQL을 알고 있으면 다양한 데이터베이스에 연결해 테이블을 생성하거나 데이터를 조회하는 등 다양하게 데이

program-yam.tistory.com

 

2024.09.06 - [데이터베이스/실습] - [DATABASE][실습] Raw Query 실습

 

[DATABASE][실습] Raw Query 실습

Node.js 에서 Raw Query를 사용하기 위해서 AWS RDS에서 대여받은 MySQL에 연결을 도와주는 데이터베이스 드라이버가 필요하다. 데이터베이스에 직접 SQL을 요청하고, 테이블을 생성하거나, 데이터를 삽

program-yam.tistory.com

 

2024.09.06 - [데이터베이스] - [DATABASE] ORM과 Prisma

 

[DATABASE] ORM과 Prisma

Prisma는 ORM ( Object Relational Mapping )으로써 Javascript 객체 ( Object )와 데이터베이스의 관계 ( Relation )을 연결 해주는 도구다. Prisma와 같은 ORM은 여러가지의 관계형 데이터베이스 ( RDB )를 사용할 수 있

program-yam.tistory.com

 

2024.09.06 - [데이터베이스/실습] - [DATABASE][실습] Prisma 시작하기

 

[DATABASE][실습] Prisma 시작하기

Prisma 라이브러리 설치# yarn 프로젝트를 초기화합니다.yarn init -y# express, prisma, @prisma/client 라이브러리를 설치합니다.yarn add express prisma @prisma/client# nodemon 라이브러리를 DevDependency로 설치합니다.yarn

program-yam.tistory.com

 

Raw Query와 ORM, Prisma에 대해 배웠다.

오늘의 목표

더보기

✔️ 프로그래머스 코테 문제 풀이

✔️ Node.js 강의 듣기

✔️ 챌린지반 OT 참여


⏱️ 오늘의 일정

9:00 ~ 10:00 - 프로그래머스 코테 문제 풀이

10:00 ~ 19:00 - Node.js 강의 듣기

19:00 ~ 20:00 - 챌린지반 OT 참여


📜 프로그래머스 코테 문제 풀이

9:00 ~ 10:00 - 프로그래머스 코테 문제 풀이

 

로또의 최고 순위와 최저 순위

https://github.com/YamSaeng/AlgorithmCodingTest/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/77484.%E2%80%85%EB%A1%9C%EB%98%90%EC%9D%98%E2%80%85%EC%B5%9C%EA%B3%A0%E2%80%85%EC%88%9C%EC%9C%84%EC%99%80%E2%80%85%EC%B5%9C%EC%A0%80%E2%80%85%EC%88%9C%EC%9C%84

 

AlgorithmCodingTest/프로그래머스/1/77484. 로또의 최고 순위와 최저 순위 at main · YamSaeng/Algorithm

This is an auto push repository for Baekjoon Online Judge created with [BaekjoonHub](https://github.com/BaekjoonHub/BaekjoonHub). - YamSaeng/AlgorithmCodingTest

github.com

 

📜 Node.js 강의 듣기

10:00 ~ 19:00 - Node.js 강의 듣기

 

숙련 1주차 강의 듣기를 시작했다. 내일까지 숙련 1주차 수업듣기와 내용 정리를 완료하고

숙련 2주차 수업을 들을 수 있도록 해야겠다.

 

관계형 데이터 베이스 RDB에 대한 내용

2024.09.05 - [데이터베이스] - [DATABASE] 관계형 데이터베이스

 

[DATABASE] 관계형 데이터베이스

관계형 데이터베이스 ( RDB )는 각 데이터를 '테이블'이라는 표 형태의 구조에 저장한다.여기서, 각 '테이블'은 여러 정보를 저장하며, '테이블'간에 연관 관계를 설정해, 여러 테이블에 분산된 데

program-yam.tistory.com

 

AWS에서 RDS를 구매하고 사용하는 방법

2024.09.05 - [IT] - [AWS] RDS 구매하고 사용하기

 

[AWS] RDS 구매하고 사용하기

RDS는 AWS에서 제공하는 관계형 데이터베이스 서비스다.RDS는 서버 운영, 유지 보수, 백업과 같이 데이터베이스 관련 작업을 AWS에게 위임해서, 백엔드 개발자가 데이터베이스를 사용하는 것에 집

program-yam.tistory.com

 

SQL 문법

2024.09.05 - [데이터베이스] - [DATABASE] SQL ( Structured Query Language )

 

[DATABASE] SQL ( Structured Query Language )

SQL데이터베이스에서 사용하는 생성, 삽입, 조회 명령문을 SQL ( Structured Query Language )라고 한다. SQL의 종류DDL ( Data Definition Language )DML ( Data Manipulation Language )DCL ( Data Control Language )TCL ( Transaction Cont

program-yam.tistory.com

 

SQL 제약조건

2024.09.05 - [데이터베이스] - [DATABASE] SQL 제약조건

 

[DATABASE] SQL 제약조건

제약 조건 ( Constraint )은 각 컬럼들간의 제안사항을 관리하고,조건을 위반하는 데이터를 방지해 데이터베이스의 무결성 ( Integrity )을 보장하는 규칙이다. 무결성은 데이터가 결함없이 정확하고

program-yam.tistory.com

 

관계형 데이터베이스에 대한 개념, SQL 문법, SQL 제약 조건 등을 배웠다.

 

📜 챌린지반 OT 참여

19:00 ~ 20:00 - 챌린지반 OT 참여

 

챌린지반 OT에 참여했다.

게임서버에 관한 깊은 내용을 다룬다는 공지를 받았고, 그만큼 어려울 것이라는 말도 함께 들었다.

신청하면 평가를 받아서 참여 여부를 알려준다고 하는데 ㅠㅠ 

부디 참여해서 수업을 듣고 싶다.

 

오늘의 목표

더보기

✔️ Node.js 강의 듣기

✔️ AWS 비용 줄이기 특강


⏱️ 오늘의 일정

9:00 ~ 19:00 Node.js 강의 듣기

19:00 ~ 20:00 AWS 비용 줄이기 특강


📜 Node.js 강의 듣기

9:00 ~ 19:00 Node.js 강의 듣기

 

오늘까지 해서 입문 강의를 모두 완강했다. 내일부터는 숙련 강의를 들을 예정

 

2024.09.04 - [Javascript] - [Javascript][Node.js] joi ( 유효성 검증 )

 

[Javascript][Node.js] joi ( 유효성 검증 )

Joi는 Javascript 유효성 검증을 위한 라이브러리다. Joi는 여러 타입과 규칙을 이용해 유효성을 검증할 수 있으며, 유효성 검증에 실패하면 오류를 발생시킨다. 유효성 검증 ( Validation )function is1(valu

program-yam.tistory.com

 

2024.09.04 - [Javascript] - [Javascript][Node.js] 에러 처리 미들웨어

 

[Javascript][Node.js] 에러 처리 미들웨어

Express.js의 에러 처리 미들웨어에러 처리 미들웨어는 Express.js가 공식적으로 제공하는 기능으로, 아래와 같은 구조를 가지고 있다.app.use((err, req, res, next) => { console.error(err.stack); res.status(500).send('So

program-yam.tistory.com

 

2024.09.04 - [Javascript] - [Javascript] Prettier ( 코드 서식 관리 도구 )

 

[Javascript] Prettier ( 코드 서식 관리 도구 )

Prettier는 코드 서식을 관리 해주는 패키지다 Prettier 사용 준비# Prettier를 debDependency로 설치합니다.yarn add prettier -D 위 명령어를 터미널에 입력해 prettier를 설치한다. ( D = 개발 환경 전용 ) Prettier

program-yam.tistory.com

 

📜 AWS 비용 줄이기 특강

19:00 ~ 20:00 AWS 비용 줄이기 특강

 

악명(?) 높은 AWS 비용에 관한 특강이 있어서 참여했다.

가랑비에 옷 젖는줄 모른다는 말처럼 나도 모르게 비용이 엄청 늘어난다는 것이 주된 내용이였다.

AWS 비용이 지정한 액수에 가까워지면 메일로 알려주는 옵션, 해킹 당하면 비트채굴용으로 쓰인다는 예시와 함께 OTP 설명을 들어서 바로 수행했다.

오늘의 목표

더보기

✔️ 프로그래머스 코테 문제 풀이

✔️ CS 강의

✔️ Node.js 강의 듣기

✔️ Javascript 피드백 작성 


⏱️ 오늘의 일정

9:00 ~ 10:00 - 프로그래머스 코테 문제 풀이
10:00 ~ 11:00 - CS 강의

12:00 ~ 16:00 - Node.js 강의 듣기

16:00 ~ 17:00 - 베이직반 OT

17:00 ~ 21:00 - Node.js 강의 듣기


📜 프로그래머스 코테 문제 풀이

9:00 ~ 10:00 - 프로그래머스 코테 문제 풀이

 

기사 단원의 무기

https://github.com/YamSaeng/AlgorithmCodingTest/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/136798.%E2%80%85%EA%B8%B0%EC%82%AC%EB%8B%A8%EC%9B%90%EC%9D%98%E2%80%85%EB%AC%B4%EA%B8%B0

 

AlgorithmCodingTest/프로그래머스/1/136798. 기사단원의 무기 at main · YamSaeng/AlgorithmCodingTest

This is an auto push repository for Baekjoon Online Judge created with [BaekjoonHub](https://github.com/BaekjoonHub/BaekjoonHub). - YamSaeng/AlgorithmCodingTest

github.com

 

풀이

function solution(number, limit, power) {
    let totalSum = 0;
    for (let i = 1; i <= number; i++) {

        let sum = 0;
        for (let j = 1; j <= i; j++) {
            if (i % j == 0) {
                sum++;
            }

            if (sum > limit) {
                sum = power;
                break;
            }
        }

        totalSum += sum;
    }

    return totalSum;
}

 

위처럼 코드를 구성해서 돌리니 시간초과가 났다..

수학공식이 필요한거 같아 구글을 뒤져보니 제곱근을 이용해 반복 수를 줄이는 방법을 찾았다.

 

function solution(number, limit, power) {
    let totalSum = 0;

    for (let i = 1; i <= number; i++)
    {
        let sum = 0;
        
        for (let j = 1; j <= Math.sqrt(i); j++)
        {
            if (i % j == 0)
            {
                if (i / j == j)
                {
                    sum += 1;
                }
                else
                {
                    sum += 2;
                }
            }

            if (sum > limit) {
                sum = power;
                break;
            }
        }

        totalSum += sum;
    }

    return totalSum;
}

 

코드로는 이해가 가는데, 원리적인 부분이 이해가 잘 안되서 

https://han-joon-hyeok.github.io/posts/programmers-knight-weapon/

 

프로그래머스 Level 1 - 기사단원의 무기 (JavaScript)

프로그래머스 - Level1 기사단원의 무기

han-joon-hyeok.github.io

 

여기 글을 참고해 이해했다.


📜 CS 강의

9:00 ~ 10:00 - CS 강의

 

컴퓨터 네트워크에 관한 수업

 

📜 베이직반 OT

16:00 ~ 17:00 - 베이직반 OT

 

2주차 수업인 Javascript에 관한 기초반에 대한 OT가 있어서 참여했다.

튜터님의 말로는 어느정도 캠프에서 제공한 Javascript 강의가 이해가 되고 진도가 나갔으면 굳이 듣지는 않아도 된다고 하셨다. 참여는 안하기로 했지만 영상과 강의 자료는 올라오면 볼 예정

 


📜 Node.js 강의 듣기

17:00 ~ 21:00 - Node.js 강의 듣기

 

MongoDB를 활용해 할일을 작성하고 DB에 저장해보는 웹 서버를 구현했다.

 

Node.js에서 사용하는 Middleware도 배웠다.

2024.09.03 - [Javascript] - [Javascript][Node.js] Middleware ( 미들웨어 )

 

[Javascript][Node.js] Middleware ( 미들웨어 )

미들웨어는 서버의 요청 ( Request ) - 응답 ( Response ) 과정에서 중간에 위치해 특정 기능을 수행하는 함수라고 볼 수 있다.예를 들어, 모든 요청에 대해 로그를 남기거나, 특정 사용자만 API를 접근

program-yam.tistory.com

 

오늘의 목표

더보기

✔️ 프로그래머스 코테 문제 풀기

✔️ Node.js 강의 듣기


⏱️ 오늘의 일정

9:00 ~ 10:00 - 프로그래머스 코테 문제 풀기
10:00 ~ 21:00 - Node.js 강의 듣기 


📜 프로그래머스 코테 문제 풀기

9:00 ~ 10:00 - 프로그래머스 코테 문제 풀기

 

덧칠하기

https://github.com/YamSaeng/AlgorithmCodingTest/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/161989.%E2%80%85%EB%8D%A7%EC%B9%A0%ED%95%98%EA%B8%B0

 

AlgorithmCodingTest/프로그래머스/1/161989. 덧칠하기 at main · YamSaeng/AlgorithmCodingTest

This is an auto push repository for Baekjoon Online Judge created with [BaekjoonHub](https://github.com/BaekjoonHub/BaekjoonHub). - YamSaeng/AlgorithmCodingTest

github.com

 

📜 프로그래머스 코테 문제 풀기

10:00 ~ 21:00 - Node.js 강의 듣기 

 

대표적인 NoSql인 MongoDB를 배우고, MongoDB Cloud를 생성하는 방법, Studio 3T라는 Mongo DB 관리 툴, 코드로 Mongo DB에 접속하고 제어하는 방법을 배웠다.

 

2024.09.02 - [Javascript] - [Javascript][Node.js] Mongo DB

 

[Javascript][Node.js] Mongo DB

MongoDB는 모든 데이터가 JSON 형태로 저장된다.복잡한 데이터 구조를 쉽게 저장할 수 있는 장점이 있다. ( 대용량 데이터를 손쉽게 처리할 수 있다 )데이터베이스 서버의 확장을 손쉽게 줄이고 늘

program-yam.tistory.com

 

2024.09.03 - [분류 전체보기] - [Javascript][Node.js] Studio 3T

 

[Javascript][Node.js] Studio 3T

Studio 3T는 MongoDB를 위해 만들어진 MongoDB Client다 https://robomongo.org/ Robo 3T | Free, open-source MongoDB GUI (formerly Robomongo)Read by Dmitry Schetnikovichrobomongo.org 위 사이트를 통해 다운 받을 수 있다. MongoDB에 연

program-yam.tistory.com

 

2024.09.03 - [Javascript] - [Javascript][Node.js] MongDB 코드에서 연결

 

[Javascript][Node.js] MongDB 코드에서 연결

MongoDB 코드에서 연결mongoose를 이용해 데이터베이스에 연결할 수 있다. mongoosemongoose는 MongoDB에 데이터를 쉽게 읽고 쓰게 해주는 JavaScript 라이브러리다. Mongoose를 ODM ( Object Document Mapper ) 이라고 부

program-yam.tistory.com

 

 

오늘의 목표

더보기

✔️ 프로그래머스 코테 문제 풀이

✔️ 주특기 입문 / 숙련 발제

✔️ Node.js 강의 듣기


⏱️ 오늘의 일정

9:00 ~ 10:00 - 프로그래머스 코테 문제 풀이
10:00 ~ 11:00 - 주특기 입문 / 숙련 발제

13:00 ~ 21:00 - Node.js 강의 듣기


📜 프로그래머스 코테 문제 풀이

9:00 ~ 10:00 - 프로그래머스 코테 문제 풀이

 

소수 만들기

https://github.com/YamSaeng/AlgorithmCodingTest/tree/main/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8%EC%8A%A4/1/12977.%E2%80%85%EC%86%8C%EC%88%98%E2%80%85%EB%A7%8C%EB%93%A4%EA%B8%B0

 

AlgorithmCodingTest/프로그래머스/1/12977. 소수 만들기 at main · YamSaeng/AlgorithmCodingTest

This is an auto push repository for Baekjoon Online Judge created with [BaekjoonHub](https://github.com/BaekjoonHub/BaekjoonHub). - YamSaeng/AlgorithmCodingTest

github.com

 

📜 주특기 입문 / 숙련 발제

10:00 ~ 11:00 - 주특기 입문 / 숙련 발제

 

다음주 부터 본격적인 주특기 입문 / 숙련 강의 주차에 진입한다고 전달받았다.

화요일 부터는 베이직반, 목요일 부터는 챌린지 반이 시작된다.

베이직 반은 실습 중심 기초 수업으로 Node.js 에 대한 필수적인 내용을 주로 학습한다.

챌린지 반은 게임 서버 심화 이론 수업으로 게임 서버 개발에 관한 심도 있는 이론과 고급 기술을 학습한다.

 

두 반 모두 참여 할 수 있다고 공지받아서 두 수업 모두 참여할 생각이다.

 

📜 Node.js 강의 듣기

13:00 ~ 21:00 - Node.js 강의 듣기

 

기초 Node.js 강의 복습을 완료했다. 아직 정리 해야할것이 남아서 정리를 마치고, 주말에는 숙련 강의를 전부 듣고

정리할 예정.

 

2024.08.30 - [Javascript] - [Javascript] API Client ( Insomnia API Client )

 

[Javascript] API Client ( Insomnia API Client )

API Client란 개발단계에서 작성한 API의 요청을 확인하거나 테스팅 할 때 도움을 주는 툴이다.API Client를 사용함으로 개발 속도를 높이거나 잘못된 API 요청으로 인한 치명적인 에러를 예방하는데

program-yam.tistory.com

 

2024.08.30 - [Javascript] - [Javascript][Node.js] Request, Response

 

[Javascript][Node.js] Request, Response

RequestRequest란 클라이언트가 서버에게 전달하려는 정보나 메세지를 담은 객체를 의미한다.Request의 세부 사항에는 URL, http method, 헤더( header ), 쿼리 파라미터 ( query parameter ), 바이 데이터 ( body data

program-yam.tistory.com

 

2024.08.30 - [Javascript] - [Javascript][Node.js] Express.js - req, res 객체

 

[Javascript][Node.js] Express.js - req, res 객체

req 객체req.app : req 객체를 통해 Express.js의 app객체에 접근한다.req.ip : 요청한 클라이언트의 ip 주소가 담겨 있다.req.body : Request를 호출할 때 body로 전달된 정보가 담긴 객체다. ( express.json() 미들웨

program-yam.tistory.com

 

+ Recent posts