728x90
반응형

 

var originText = "This is example text.";
console.log("Original : ", originText);

// Base64 Encoding
base64EncodedText = Buffer.from(originText, "utf8").toString('base64');
console.log("Base64 Encoded Text : ", base64EncodedText);

// Base64 Decoding
base64DecodedText = Buffer.from(base64EncodedText, "base64").toString('utf8');
console.log("Base64 Decoded Text : ", base64DecodedText);

 

728x90
반응형
728x90
반응형

CentOS

$ sudo yum install net-tools

 

Ubuntu

$ sudo apt-get install net-tools

 

이후에 명령어 실행

$ sudo netstat -tnlp
728x90
반응형
728x90
반응형

브라우저에 접속한 사용자의 IP 주소를 확인하는 방법은 생각보다 간단하다.

 

하단의 사이트에서 그 기능을 지원해주는데, get 메소드로 쉽게 얻을 수 있다.

 

https://geolocation-db.com/

 

Geolocation DB - Geographic location By IP Address

 

geolocation-db.com

 

1. 먼저 axios 를 설치한다.

$npm i axios

 

2. 사용한다. ( hook 이나 함수 안에서 사용 )

const res = axios.get('https://geolocation-db.com/json/')
              .then((res) => {
                console.log("data : ", res)
              })

 

3. 결과값을 확인해본다.

IP 주소 뿐만 아니라 국가와 도시까지 알 수 있다.

728x90
반응형
728x90
반응형

쿠키와 세션만을 이용한 중복 로그인 방지를 구현할 때에는 몇가지 단점이 있었다.

  • 로그인을 한 상태에서 ( 세션은 유효 ) 사용자가 쿠키를 임의로 삭제할 경우
  • 기존 로그인 된 브라우저에서 ( 세션은 유효 ) 로그아웃을 안하고 다른 브라우저에 접속할 때

이러한 상황에서 사용자는 분명 로그아웃을 한거 같은데 이미 접속중인 사용자라고 뜰 것이다.

물론 개발자야 왜 그런지 이유를 알지만 쿠키와 세션에 관한 이해도가 없는 일반 사용자같을 경우엔 이유를 모르기 때문에 문제가 발생한다.

하지만 jwt를 생성한 뒤 세션에 토큰에 대한 내용을 담고, Next.js의 SSR에서 자신이 가지고 있는 토큰과 세션이 가진 토큰이 같은지 확인하고 다르면 ( 다른 사용자가 로그인을 시도하고 토큰의 내용을 수정할 경우 ) 로그아웃을 진행하면 이러한 문제를 사전에 방지할 수 있다. 

 

 

1. 먼저 서버에 필요한 npm 패키지를 설치한다.

$npm i jsonwebtoken

 

2. secretKey를 위해 파일을 만들고 다음과 같이 작성해준다.

config/jwt.js

let jwtObj = {};

jwtObj.secret = "key"

module.exports = jwtObj

 

3.  jwt 사용이 필요한 (로그인 api) 라우터에 선언을 해준다.

const jwt = require('jsonwebtoken');
const secretObj = require('../config/jwt');

 

4. 세션을 위한 준비를 해준다.

https://typo.tistory.com/entry/MySQL-%EC%84%B8%EC%85%98%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%A4%91%EB%B3%B5-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%B0%A9%EC%A7%80-with-sequelize

 

MySQL | 세션을 이용한 중복 로그인 방지 with sequelize

회사에서 사용자들 수에 대한 제한을 두어서 중복 로그인 방지를 구현하게 되었다. 쿠키는 브라우저에만 남기에 중복 로그인을 쿠키로만으로는 구현할 수 없다. 세션은 데이터베이스에 저장이

typo.tistory.com

 


db 조회 시 필요한 sequelize ( 자세한 내용은 블로그 참조 바랍니다. 이번 포스트에서는 raw query를 사용했습니다. )

const { QueryTypes, sequelize } = require('sequelize');
const db = require('../sequelize/models');

 


 

5. login 라우터에  token을 세션과 쿠키에 주입하는 문구를 추가해준다.

router.get("/login", function (req, res, next) {

	// 로그인 정보가 있는지 확인 
    
    	...
        
    if(로그인정보가 없으면) {
    		return res.send({success : false});
    }
    else {
       
	// 조회한 로그인 정보가 세션이 존재하는지 확인
    const select_data_query = `SELECT JSON_EXTRACT(data, '$.token') as token FROM SESSIONS WHERE JSON_EXTRACT(data, '$.uid') = :uid`

          db.sequelize.query(
            select_data_query,
            {
              replacements: { uid: 로그인정보.user_id }, // 위에 로그인 정보 있는지 확인해서 취합한 object ( db에 user_id로 저장되있음 )
              type: QueryTypes.SELECT
            }
          ).then((result) => {
            // 세션이 존재하지 않을 경우
            if(result.length === 0) {
            	const cookie = {
                	uid: 로그인정보.user_id
                }
                
                // jwt 토큰 생성
  		const token = jwt.sign(
    		{ isLogined: cookie },
    		secretObj.secret,
    		{ expiresIn: '86400000' }
  		);
        
        	// 쿠키 생성
		res.cookie('isLogined', token, { maxAge: 86400000, path: "/", httpOnly: true }); // jwt 토큰을 쿠키에 담아서 생성
  		
        	// 세션 생성
        	req.session.uid = 로그인정보.user_id; 
  		req.session.token = token;
                
                res.send({success : true})
            }
            // 세션이 존재할 경우
            else {
            	return res.send({success : false, result : "isLogined"})
                }
          })
     }
});

 

 

6. client에서 세션이 존재할 경우 alert 창을 띄우고 기존 사용자를 로그아웃 할 것인지 물어보는 로직을 구성한다.

  const loginSubmit = (e: any) => {
    e.preventDefault();

    axios.post('/api/session/login', {
      user_id: id,
      user_password: pw
    }).then(({ data }) => {
      if (data.success) {
        router.push('/view/labor');
      }
      else if (data.result === "isLogined") {
        if (window.confirm('이미 로그인중인 사용자가 있습니다. 접속을 해제하시겠습니까?')) {
          axios.post('/api/session/disconnect', {
            user_id: id,
            user_password: pw
          })
            .then(({ data }) => {
              if (data.success) {
                router.push('/view/labor');
              }
            })
        }
      }
      else {
        alert('존재하지 않는 사용자입니다.');
      }
    })
  };

 

7. 만약 이미 접속중인 사용자를 로그아웃하고 본인이 로그인하길 원하면 세션 내의 토큰을 재발급해준다.

router.post('/disconnect', function (req, res, next) {

  // 로그인 정보가 있는지 확인 
    
    	...

  if(로그인정보가 없으면) {
    return res.send({success : false});
  }
  else {
  
  	const cookie = {
    		name: 로그인정보.name // 위에 로그인 정보가 있는지 확인한 곳에서 가져온 데이터
    	}
        
        // 토큰 발급
        const token = jwt.sign(
        { isLogined: cookie },
        secretObj.secret,
        { expiresIn: '86400000' }
      );
        
        // 세션 db를 조회하여 전에 발급한 토큰이 있는지 확인
        
        const select_data_query = `SELECT JSON_EXTRACT(data, '$.token') as uid FROM SESSIONS WHERE (JSON_EXTRACT(data, '$.uid') = :uid AND JSON_EXTRACT(data, '$.token') = :token)`

      db.sequelize.query(
        select_data_query,
        {
          replacements: { uid: 로그인정보.user_id, token: token },
          type: QueryTypes.SELECT
        }
      ).then((result) => {
        // 쿠키를 임의로 삭제하고 다시 로그인할 경우엔 세션 쿠키 안에 토큰 내용이 없다. 세션을 만들어주어야 한다.
        if (result.length === 0) {

          // 기존에 있던 세션의 token 값을 바꿔준다.
          let sql_update_data = "UPDATE SESSIONS SET data = JSON_SET(data, '$.token', :token) WHERE JSON_EXTRACT(data, '$.uid') = :uid";

          db.sequelize.query(
            sql_update_data,
            {
              replacements: { uid: 로그인정보.user_id, token: token },
              type: QueryTypes.UPDATE
            }
          ).then((result) => {
            console.log("result : ", result)
          });

          req.session.uid = 로그인정보.user_id; // 세션 생성
          req.session.token = token;

          res.cookie('isLogined', token, { maxAge: 86400000, path: "/", httpOnly: true }); // jwt 토큰을 쿠키에 담아서 생성

          res.send({ success: true })


        }
        // 쿠키를 임의로 삭제하지 않았을 경우엔 세션을 따로 만들 필요가 없다.
        else {
          // 기존에 있던 세션의 token 값을 바꿔준다.
          let sql_update_data = "UPDATE SESSIONS SET data = JSON_SET(data, '$.token', :token) WHERE JSON_EXTRACT(data, '$.uid') = :uid";

          db.sequelize.query(
            sql_update_data,
            {
              replacements: { uid: 로그인정보.user_id, token: token },
              type: QueryTypes.UPDATE
            }
          ).then((result) => {
            console.log("result : ", result)
          });

          res.cookie('isLogined', token, { maxAge: 86400000, path: "/", httpOnly: true }); // jwt 토큰을 쿠키에 담아서 생성

          res.send({ success: true })

        }
      })
  }
})

로그인 정보가 존재할 경우에 접속을 해제할 경우의 수가 2가지 있다.

  1. 쿠키를 삭제한 뒤 재접속 할 경우 ( 세션 쿠키 자체는 존재하지만 임의로 만들어준 uid 데이터가 없다. )
  2. 로그아웃 한 뒤 재접속 할 경우 ( 세션 쿠키 자체가 존재하지 않다. )

첫번째의 경우 데이터가 사라진 상태기 때문에 세션을 생성해주어야 하고, 두번째는 필요없다.

 

 

8. getServerSideProps에서 쿠키가 없을 경우 redirect 시킨다.

( 로그인중이 아니였고 쿠키를 제거하고 로그인을 시도할 경우 방지)

export const getServerSideProps: GetServerSideProps = async (context) => {
  const isLogined = context.req.cookies.isLogined ? true : false;
  
  // 쿠키가 존재할 경우
  if (isLogined) {
  
  // 쿠키 안의 토큰을 decoded한 값
  const tokenValue = parseJwt(context.req.cookies.isLogined);
  
  	return {
          props: {
            cate,
            tokenValue
          },
        };
  
  }
  // 쿠키가 존재하지 않을 경우
  else {
  	return {
      	 redirect: {
        	permanent: false,
        	destination: "/",
      	},
    };
  }

 

8. 클라이언트에서 useEffect hook으로 접속한 사용자의 정보를 계속 확인해준다.

(자신이 발급한 토큰이 세션에 있는 토큰과 일치한지, 로그인 해 둔 상태에서 쿠키를 제거했을 때 redirect)

  useEffect(() => {
    axios.post('/api/session/islogined')
      .then(({ data }) => {
        if (data.success) {
          alert('다른 곳에서 로그인하여 로그아웃 됩니다.')
          axios.get('/api/session/isloginedout')
          router.push('/')
        }
        else if (data.result === 'notcookie') {
          alert('쿠키가 삭제되어 로그아웃 됩니다.')
          axios.get('/api/session/logout')
          router.push('/')
        }
        else {
        }
      })
  })

 

9. 세션 안에 내가 발급했던 토큰 값이 잘 들어가있는지 확인한다. ( islogined router )

// client SSR에서 세션에 로그인 데이터가 있는지 확인한다.
router.post('/islogined', function (req, res, next) {
  const cookie = req.cookies;

  try {
    // 쿠키 또는 세션이 존재하지 않을 경우
    if (!cookie.isLogined) {
      return res.send({ success: false, result: "notcookie" })
    }
    else {
      // 쿠키가 존재할 경우 브라우저 세션(쿠키) 안의 토큰을 decoded하여 db의 세션 안의 token값을 비교해본다.
      const verify = jwt.verify(
        cookie.isLogined,
        secretObj.secret,
        { expiresIn: '86400000' }
      )


        const select_data_query = `SELECT JSON_EXTRACT(data, '$.token') as data FROM SESSIONS WHERE (JSON_EXTRACT(data, '$.uid') = :uid AND JSON_EXTRACT(data, '$.token') = :token)`

	// 내가 발급했던 uid와 token이 잘 있는지 확인한다. ( 다른 사용자가 토큰을 재발급하지 않았는지 )
        db.sequelize.query(
          select_data_query,
          {
            replacements: { uid: verify.isLogined.user_id, token: cookie.isLogined },
            type: QueryTypes.SELECT
          }
        ).then((result) => {
          if (result.length === 0) {
            return res.send({ success: true });
          }
          else {
            return res.send({ success: false })
          }
        })
    }
  }
  catch (err) {
    console.log(err)

  }
})

 

 

 사용 도중 로그아웃이 될 경우의 수는

  1. 사용 도중 다른 곳에서 로그인을 했을 경우
  2. 사용 도중 쿠키를 삭제할 경우

2가지가 있다.

10. 각각의 경우를 고려하여 라우터를 만들어준다.

1) 다른 접속자가 로그인을 할 경우

// 다른 접속자가 로그인을 할 경우
router.get('/isloginedout', function (req, res, next) {

    // 토큰만 삭제
    delete req.session.token;

    // 쿠키 삭제
    res.clearCookie('isLogined');

    res.send({ success: true }); // 클라이언트에 결과 반환
})

쿠키와 토큰을 삭제해준다.

 

2) 쿠키가 삭제되거나 로그아웃을 시도할 경우

router.get('/logout', function (req, res, next) {
  console.log("req. : ", req.session)

  if (req.session.uid) {

    const delete_data_query = `DELETE FROM SESSIONS WHERE JSON_EXTRACT(data, '$.uid') = :uid`

    db.sequelize.query(
      delete_data_query,
      {
        replacements: { uid: req.session.uid },
        type: QueryTypes.DELETE
      }
    ).then((result) => {

      // 세션 삭제
      delete req.session.uid;
      delete req.session.token;

      // 쿠키 삭제
      res.clearCookie('isLogined');

      res.send({ success: true }); // 클라이언트에 결과 반환

    }).catch((err) => {
      console.log(err)
    })
  }
  else {
    // 세션 삭제

    delete req.session.token;

    // 쿠키 삭제
    res.clearCookie('isLogined');

    res.send({ success: true }); // 클라이언트에 결과 반환
  }
})

쿠키가 삭제되거나 로그아웃을 했을 경우엔 데이터베이스에서 해당 값을 아예 삭제해버리고

세션 또한 삭제해준다.

728x90
반응형
728x90
반응형

1. 라이브러리 설치

npm i -D @typescript-eslint/parser @typescript-eslint/eslint-plugin

 

2. .eslintrc.json 파일 수정

{
  "plugins": [
    "@typescript-eslint"
  ],
  "extends": [
    "plugin:@typescript-eslint/eslint-recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  "parser": "@typescript-eslint/parser",
  "rules": {
    "@typescript-eslint/no-empty-interface": "off",
    "no-empty-interface": "off"
  },
  "overrides": [
    {
      "files": [
        "*.js",
        "*.jsx"
      ],
      "rules": {
        "@typescript-eslint/explicit-function-return-type": "off",
        "@typescript-eslint/no-unused-vars": "off",
        "@typescript-eslint/no-empty-interface": "off"
      }
    }
  ]
}
728x90
반응형
728x90
반응형

사이트에 접속해서 개인정보처리방침 만들기를 한다.

https://www.privacy.go.kr/a3sc/per/inf/perInfStep01.do

 

> 메인

개인정보 처리방침 만들기 개인정보 처리방침 간단히 만들기 개인정보 처리방침을 간단하게 작성해 보세요. 개인정보 처리방침 새로 만들기 - 개인정보보호법 및 시행령, 표준 개인정보 보호지

www.privacy.go.kr

 

 

아래 다운로드 하여 가이드 라인을 확인할 수도 있다.

728x90
반응형

'Others' 카테고리의 다른 글

Others | Unix TimeStamp 유닉스 시간 변환  (0) 2022.05.16
CentOS, Ubuntu netstat 설치 방법  (0) 2022.03.15
nginx 설치 및 방화벽 구성(http)  (0) 2022.02.16
Others | postman  (0) 2021.09.29
whatch error 해결법  (0) 2021.08.31
728x90
반응형

회사에서 사용자들 수에 대한 제한을 두어서 중복 로그인 방지를 구현하게 되었다.

쿠키는 브라우저에만 남기에 중복 로그인을 쿠키로만으로는 구현할 수 없다.

 

세션은 데이터베이스에 저장이 되기 때문에 많은 정보를 담으면 서버에 과부하가 올 수 있다. 때문에 길이가 긴 데이터들은 jwt를 이용하였고, 키 값인 아이디만 세션에 두고 로그인 중인 사용자가 있으면 중복 로그인을 방지하게끔 구현했다. 

 

1. 세션 관련 모듈을 설치한다.

$npm i express-session --save
$npm i express-mysql-session -- save

 

2. app.js에 다음과 같이 추가해준다.

 ... 
var session = require('express-session');
var MySQLStore = require('express-mysql-session')(session);

 ...

var option = {
    host: process.env.DB_HOST,
    user: process.env.DB_USER,
    password: process.env.DB_PW,
    port: process.env.DB_PORT,
    database: process.env.DB_DATABASE,
    clearExpired: true,
    checkExpirationInterval: 10000,
    expiration: 10000
}

var sessionStore = new MySQLStore(option);

app.use(session({
  secret: process.env.secret,
  resave: false,
  saveUninitialized: true,
  store: sessionStore
  
}))

option 중 세션에 관한 내용을 살펴보면,

  • clearExpired : 유효기간이 지난 세션을 삭제한다.
  • checkExpirationInterval : 세션의 유효기간을 체크하는 시간을 설정한다.
  • expiration : 세션의 유효기간을 설정한다.

 

3. 라우터에서의 사용법은 다음과 같다.

router.get("/", function (req, res, next) {
  
  req.session.uid = "uid" // 세션 생성
  delete req.session.uid // 세션 삭제
  
  res.send({success : true })
});

 

보통은 로그인을 한 정보를 쿠키에 담는다. 그 후에 로그인 정보가 쿠키에 있으면 별도의 로그인 화면 없이 바로 메인 화면으로 가게끔 편의성을 위해 구현해둔 경우도 있다.

 

먼저 우리는 로그인을 할 때 쿠키에 로그인 정보를 담기 이전에 세션에 이미 로그인 중인 데이터가 있는지 확인할 것이다.

그리고 데이터가 없으면 쿠키에 로그인 정보를 담고 로그인에 성공한다.

 

세션이 생기는 경로는 로그인밖에 없지만 세션이 삭제되어야하는 경우는 두 가지가 있다.

  1. 로그아웃을 해서 세션을 삭제
  2. 세션의 expiration 시간이 지나면 자동으로 삭제

이미 위에서 세션의 유효시간을 설정했기 때문에 두 번째의 경우는 설정이 따로 필요없다. 하지만 로그아웃 버튼을 클릭했을 때는 우리가 직접 쿼리로 삭제해주어야 한다.

 

그러기 위해 먼저 sequelize model에 sessions 파일을 추가해준다. sequelize에 대한 내용은 다음 포스트를 참고하자.

 

https://typo.tistory.com/entry/MySQL-CRUD-with-Reactjs-and-Nodejs-Back-End1?category=905676

 

MySQL | CRUD with Node.js | Sequelize

Server.js 먼저 백엔드 서버를 express로 실행하기위해 설치해준다. $ npm install express 그다음 package.json에 스크립트 서버를 실행하는 명령어를 추가해준다. server/package.json { "scripts": { "start":..

typo.tistory.com

 

sequelize/models/sessions.js 

module.exports = (sequelize, DataTypes) => {

    const Sessions = sequelize.define("Sessions", {
        session_id: {
            type: DataTypes.STRING,
            primaryKey: true,
        },
        expires: {
            type: DataTypes.STRING
        },
        data: {
            type: DataTypes.STRING
        },
    }, {
        charset: "utf8", // 한국어 설정
        collate: "utf8_general_ci", // 한국어 설정
        tableName: "sessions", // 테이블 이름
        timestamps: false, // createAt & updateAt 활성화
    });
    return Sessions;
};

 

4. 로그인과 로그아웃을 구성해준다.

const { QueryTypes, sequelize } = require('sequelize');
const db = require('../sequelize/models');


router.get("/login", function (req, res, next) {

	// 로그인 정보가 있는지 확인 
    
    	...
        
	// 세션이 존재하는지 확인
    const select_data_query = `SELECT JSON_EXTRACT(data, '$.uid') as uid FROM SESSIONS WHERE JSON_EXTRACT(data, '$.uid') = :uid`

          db.sequelize.query(
            select_data_query,
            {
              replacements: { uid: "teepo" },
              type: QueryTypes.SELECT
            }
          ).then((result) => {
            // 세션이 존재하지 않을 경우
            if(result.length === 0) {
            
                // 세션 생성
                req.session.uid = "teepo"
                
                // jwt 토큰 사용자면 jwt 토큰 생성
                
                // 쿠키 생성
                res.cookie('isLogined', {"uid": "teepo"}, { maxAge: 86400000, path: "/", httpOnly: true });
                
                res.send({success : true})
            }
            // 세션이 존재할 경우
            else {
            	return res.send({success : false, result : "이미 접속중인 사용자입니다."})
                }
          })
});

router.get("/logout", function (req, res, next) {
  
  // 세션에 존재하는 uid 데이터와 일치하는 데이터 삭제
  const delete_data_query = `DELETE FROM SESSIONS WHERE JSON_EXTRACT(data, '$.uid') = :uid`

  db.sequelize.query(
    delete_data_query,
    {
      replacements: { uid: req.session.uid },
      type: QueryTypes.DELETE
    }
  ).then((result) => {

    // 세션 삭제
    delete req.session.uid;

    // 쿠키 삭제
    res.clearCookie('isLogined');

    res.send({ success: true }); // 클라이언트에 결과 반환

  }).catch((err) => {
    console.log(err)
  })
});

 

728x90
반응형

'DB > MySQL' 카테고리의 다른 글

MySQL | Connection 문제 해결  (0) 2021.12.21
MySOL | MariaDB timeout 설정  (0) 2021.12.20
MySQL | CRUD with Node.js | Update and Delete  (0) 2021.12.09
MySQL | CRUD with Node.js | Find  (0) 2021.12.08
MySQL | CRUD with Node.js | Create  (0) 2021.12.08
728x90
반응형

이전 포스트에서 Redux-toolkit을 이용하여 상태관리하는 법을 알아보았다.

이번 포스트에서는 createAsyncThunk로 비동기 API를 다루어보자.

 

1. 이전 포스트에서 함수를 추가한다.

import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'
import axios from 'axios'

// 인터페이스
export interface UserInterface {
    number : Number;
}


// state 초기값
const initialState: UserInterface = {
    number = 0;
}

// createAsuncThunk function
export const refreshList = createAsyncThunk(
    'user/refreshList',
    async (body: any, thunkAPI) => {
        console.log("body : ", body)
        try {
            const response = await axios.get('/api/user/', { params: { user__id: "teepo" } })
            return (await response.data)
        }
        catch (Err) {
            return thunkAPI.rejectWithValue(Err)
        }
    }
)

export const userSlice = createSlice({
    name: 'user',
    initialState,
    reducers: {
    },
    extraReducers: (builder) => {
    	// pending
    builder.addCase(refreshList.pending, (state, action) => {})
    	// fulfilled
        .addCase(refreshList.fulfilled, (state, action) => {})
		// rejected
        .addCase(refreshList.rejected, (state, action) => {})
    }
})

// 액션의 이름을 담아준다 ( CreateSlice 으로 선언한 액션은 생략 가능 !)
export const { } = userSlice.actions;

export default userSlice.reducer;

 

createAsyncThunk는 3가지의 상태값을 가진다. 각각의 상황에 맞게 로직을 구성해주면 된다.

  • pending : 대기
  • fulfilled : 이행
  • rejected : 거부

 

컴포넌트에서의 사용은 다음과 같다.

 ...
 
import { useDispatch, useSelector } from "react-redux";
import { refreshList } from "../../../../store/reducers/user/userSlice";

 ...
 
 const userState = useSelector((state: RootState) => state.user)
 const userDispatch = useDispatch<AppDispatch>();
 
  ...
  
     useEffect(() => {
        refreshListHandler(firstRefreshBody(loginData.company_code, postNumber, 0));
        try {
            userDispatch(refreshList({ aa: "dd" })).then(({ payload }: any) => {
                console.log("222", payload)
            })
        }
        catch (err) {
            console.log(err)
        }
    }, []);

 

action 함수에 원하는 파라미터값을 전달하고 axios를 실행한 뒤 결과값을 다시 반환해주는 시스템이다.

 

구글링할 때 잘 안나왔는데 typescript에서 AsyncThunkAction 쪽에서 자꾸 then이 없다는 에러가 떴다.

useDispatch<AppDispatch>를 추가하니 에러가 사라졌다.

 

728x90
반응형
728x90
반응형

이번 포스트에는 리덕스 미들웨어중 하나인 리덕스 툴킷에 대해 알아보겠다.

전에는 Redux-Thunk 를 사용하였지만 비교를 위해서도 그렇고 사용하기 편하고 확장성이 유용하다 하여 

툴킷을 사용해보게 됐다.

 

1. 먼저 필요한 모듈들을 설치한다.

$npm i react-redux next-redux-wrapper @reduxjs/toolkit
$npm i -D @types/react-redux typescript

 

2. src 폴더 밑에 store 폴더를 만들고 다음과 같이 정의 할 state를 추가해준다.

( createSlice 함수로 만들기에 파일 이름 뒤에 Slice를 붙였다.)

 

src/store/features/user/userSlice

import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'
import axios from 'axios'

// 인터페이스
export interface UserInterface {
    number : Number;
}


// state 초기값
const initialState: UserInterface = {
    number = 0;
}

export const userSlice = createSlice({
    name: 'user',
    initialState,
    reducers: {
        increment: (state: UserInterface) => {
            state.number += 1;
        },
    },
})

// 액션의 이름을 담아준다 ( CreateSlice 으로 선언한 액션은 생략 가능 !)
export const { increment } = userSlice.actions;

export default userSlice.reducer;

 

3.  store 폴더 밑에 index.ts파일을 만들고 store를 정의해준다.

이 때 중요한 점은 nextJS에서 쓸 땐 createWrapper 메소드로 wrapper를 만들어주어야 한다.

src/store/index.ts

import { configureStore } from '@reduxjs/toolkit'
import { createWrapper } from 'next-redux-wrapper';
import userSlice from './features/user/userSlice'

export const store: any = () => configureStore({
    reducer: {
        user: userSlice
    },
})

// nextjs 에서 쓸 때는 wrapper를 만들어주어야 한다!
export const wrapper = createWrapper(store, {
    debug: process.env.NODE_ENV !== "production",
});

export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch

 

 

4. _app.tsx 폴더에서 Component 에 wrapper를 씌어준다.

pages/_app.tsx

import "../styles/globals.css";
import type { AppProps } from "next/app";
import { NextPage } from "next";
import React from "react";
import { wrapper } from '../src/store' // redux-store



const MyApp: NextPage<AppProps> = ({ Component, pageProps }: AppProps) => {
  return (
    <React.StrictMode>
        <Component {...pageProps} />
    </React.StrictMode>
  );
};
export default wrapper.withRedux(MyApp);

 

5. 이제 원하는 곳에 useDispatch와 useSelector를 이용해 상태를 관리할 수 있다!

 ...
 
 import { useDispatch, useSelector } from "react-redux";

 ...
 
const userState = useSelector((state: RootState) => state.user)
const userDispatch = useDispatch();

 ...

useEffect(()=>{
	userDispatch(increment());
},[])
728x90
반응형
728x90
반응형

1. 먼저 업데이트를 실행 후 nginx를 설치해준다.

$sudo apt update
$sudo apt install nginx

 

 

2. 방화벽을 설정해준다.

$sudo ufw allow 'Nginx Full'

 

$sudo ufw status

 

3. 설정 파일을 확인해준다.

$vi /etc/nginx/sites-available/default

 

https 설정이 없는 경우 나머지를 다 지우고 proxy 할 포트와 설정만 해준다.

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        # SSL configuration
        #
        # listen 443 ssl default_server;
        # listen [::]:443 ssl default_server;
        #
        # Note: You should disable gzip for SSL traffic.
        # See: https://bugs.debian.org/773332
        #
        # Read up on ssl_ciphers to ensure a secure configuration.
        # See: https://bugs.debian.org/765782
        #
        # Self signed certs generated by the ssl-cert package
        # Don't use them in a production server!
        #
        # include snippets/snakeoil.conf;

        #root /var/www/html;

        # Add index.php to the list if you are using PHP
        #index index.html index.htm index.nginx-debian.html;

        #server_name _;

        location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;

        }

        # pass PHP scripts to FastCGI server
        #
        #location ~ \.php$ {
        #       include snippets/fastcgi-php.conf;
        #
        #       # With php-fpm (or other unix sockets):
        #       fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
        #       # With php-cgi (or other tcp sockets):
        #       fastcgi_pass 127.0.0.1:9000;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #       deny all;
        #}
}

 

4. nginx 상태를 확인 

$sudo nginx -t

 

5. nginx 다시 시작

$sudo systemctl restart nginx
728x90
반응형

'Others' 카테고리의 다른 글

CentOS, Ubuntu netstat 설치 방법  (0) 2022.03.15
Others | 개인정보처리방침 만들기 (개인정보보호법)  (0) 2022.02.24
Others | postman  (0) 2021.09.29
whatch error 해결법  (0) 2021.08.31
App-Backend 데이터 전송  (0) 2021.08.31

+ Recent posts