728x90

MySql

 

MySql은 관계형 DBMS(Relational DBMS) 중 하나이다.

 

예를 들어, 쇼핑몰에서 a, b, c라는 제품을 판매하고 있고, a', b', c'라는 고객이 있다고 하자. 

 

고객 a'와 b'는 아래와 같이 제품을 주문하였다고 할때, 쇼핑몰에서는 제품 a를 주문한 고객이 누구누구인지 알고싶다고 하면 고객 테이블과 제품 테이블의 관계를 보는 것이 가장 좋을 것이다.

 

(관계가 그려져 있지 않다면 모든 고객을 조회하여, 어떤 제품을 주문했는지 확인해야 하고, 그 제품들 속에 a가 있는지 확인해야 하는 등,,, 아주 복잡해질 것이다.) 그래서 이렇게 테이블 간의 관계가 필요할 때, 관계형 DBMS를 사용한다. 

 

 

Sequelize 

 

관계형 DB를 조작하기 위해서는, SQL(Structured Query Language)이라는 프로그래밍 언어를 사용해야 하지만,

 

Sequelize를 사용하면 SQL을 사용하지 않고도, DB를 조작할 수 있다

 

Sequelize javascript 코드를 알아서 SQL로 바꿔준다. 즉, Sequelize는 db 조작 언어이다. 

 

Sequelize 사용해보기

터미널에 아래 코드를 입력하면 된다. 

 

npm i sequelize sequelize-cli mysql2 
npx sequelize init

 

sequelize-clisequelize 명령어를 실행하기 위한 패키지이고, 

 

mysql2MySql과 Sequelize(노드, 즉, javascript 코드)를 연결해주는 드라이버이다. 

 

이들을 설치하고, sequelize init을 하면 config, models와 같은 폴더가 만들어진다. 

 

config/config.json(or config.js)는 데이터베이스 설정을 해주는 파일이다. 

db 이름, username, password 등 데이터베이스 설정은 config 폴더에서 관리하면 된다. 

 

MySql의 테이블이 Sequelize에서는 models이다. 

 

아래는 models 폴더의 index.js 파일이다. 

 

Sequelize시퀄라이즈 패키지이자 생성자이다. 

 

'config/config'에서 데이터 베이스 설정을 불러온 후, new Sequelize를 통해 노드와 MySql을 연결해준다. 

 

// models/index.js

const Sequelize = require('sequelize');
const env = process.env.NODE_ENV || 'development';
const config = require('../config/config')[env];
const db = {};

const sequelize = new Sequelize(
  config.database,
  config.username,
  config.password,
  config
);

db.Comment = require('./comment')(sequelize, Sequelize);
db.Hashtag = require('./hashtag')(sequelize, Sequelize);
db.Image = require('./image')(sequelize, Sequelize);
db.Post = require('./post')(sequelize, Sequelize);
db.User = require('./user')(sequelize, Sequelize);

Object.keys(db).forEach((modelName) => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

 

 

위의 models/index.js에서 노드와 MySql을 연결해주었으니

app.js에서 sync 메서드를 사용해, 서버 실행시 MySql과 연동되도록 하면 된다. 

 

// app.js
const express = require('express'); 

// ...생략

const db = require('./models');

// ...생략

db.sequelize
  .sync()
  .then(() => {
    console.log('db 연결 성공!');
  })
  .catch(console.error);
  
// ...생략

 

 

728x90

'Back-end' 카테고리의 다른 글

Node.js란  (0) 2021.05.14
728x90

백엔드 작업을 위해 Node를 사용하기 때문에 Node가 서버라고 오해하기 쉽지만, Node는 서버가 아니다

 

Node 공식 홈페이지에도 나와있듯, Node는 javascript runtime이다.

 

node는 javascript runtime

 

javascript runtimejavascript 코드를 실행할 수 있도록 해준다. 

 

 

그렇다면 javascript로 서버는 어떻게 구동하냐? 

 

노드는 http라는 모듈을 제공한다.  app.js를 실행하는 순간,

javascript runtime인 노드가 아래의 코드를 실행해서 http가 서버 역할을 수행하게 된다. 

 

// app.js
const http = require('http'); 
const server = http.createServer(() => {

}); 
server.listen(3065); 

 

 

 

 

728x90

'Back-end' 카테고리의 다른 글

MySql과 Sequelize  (0) 2021.05.14
728x90

아래 포스팅에 자세히 설명을 해두었다. 

 

dolphinsarah.tistory.com/21?category=467228

 

[TIL_개발일기_210322] React에서 map 사용 시 key를 설정해줘야 하는 이유는

*하루동안 새롭게 알게된 부분, 에러를 해결한 방법 등을 작성하는 개발일기입니다. 다른 사람에게도 설명해줄 수 있도록 제 머릿속에 넣기 위해 정리를 시작하게 되었습니다. React에서 map 사

dolphinsarah.tistory.com

 

그래도 간단히 설명해보자면, map을 도는 배열의 key를 고유한 값으로 설정하라고 하는 것은,

 

그렇게 하지 않았을 때 => 배열 전체가 리렌더링 될 필요가 없음에도 리렌더링 되는 것을 방지하기 위해서다.  

 

이를테면, push를 통해 아이템이 배열 끝에 추가될 경우, 마지막 요소만 변경되었으니 전체를 리렌더링할 필요가 없는 경우

 

728x90
728x90

아래와 같이, 복사하기 버튼을 눌렀을 때 클립보드에 텍스트를 복사하려면 어떻게 해야할까? 

 

 

Document.execCommand()를 사용하거나 Clipboard API를 활용하는 방법도 있다.  

 

Document.exeCommand()

 

아래는 exeCommand()에 대한 MDN의 설명이다. 

 

When an HTML document has been switched to designMode, its document object exposes an execCommand method to run commands that manipulate the current editable region, such as form inputs or contentEditable elements.

 

즉, HTML document가 디자인 모드로 바뀌면서 exeCommand를 사용하면, form의 input과 같이 편집이 가능한 element를 조작할 수 있다는 것이다. 그래서 element의 내용을 클립보드로 복사할 수 있게 되었다고 이해하면 좋을 것 같다. 

 

따라서, 어떤 element의 innertext를 복사하고자 한다면, 그 element가 editable한 input이나 textarea여야 한다. 

 

만약, 해당 element가 input이나 textarea가 아니라면, 아래의 코드처럼 textarea를 만들었다가 지우는 방식을 사용하면 된다. 

 

    const pinyin = pinyinRef.current.innerText;
    const textField = document.createElement('textarea');
    textField.innerText = pinyin;
    document.body.appendChild(textField);
    textField.select();
    document.execCommand('copy');
    textField.remove();

 

pinyinRef가 내가 복사하려고 하는 text를 가지고 있는 element를 참조하는 ref 객체이다. 

 

그래서 그 pinyinRef.current의 innerText(복사하고자하는 텍스트)를 가져와서 textField라는 임시 textarea의 innerText로 넣어준다. 

 

document에 임시 textarea를 붙여주고, 그 textarea를 select하여 복사하고자 하는 텍스트를 선택하고, 

 

document.execCommand('copy')를 사용하여 해당 텍스트를 클립보드에 복사한다. 

 

그리고 다시, 임시 textarea를 지워주면 된다. 

 

하지만, Document.execCommand()는 MDN 문서에서 알 수 있듯 더 이상 추천하지 않는 방식이다. 

 

대신, clipboardAPI를 사용한다. 

 

 

Clipboard API

 

Clipboard API는 Promise 기반으로 클립 보드 내용을 비동기적으로 접근할 수 있는 API이다. 

 

아주 아주 간단하다. 

 

		const pinyin = pinyinRef.current.innerText;
		navigator.clipboard.writeText(pinyin);

 

navigator.clipboard.writeText 만을 사용해서 가능하다. 즉, 붙여넣고자 하는 text를 인자로 넣어주면 된다. 

 

728x90

'Front-end > React' 카테고리의 다른 글

next에 antd 적용 안 될 때  (0) 2021.04.10
Redux가 필요한 이유와 기본 개념  (0) 2020.11.14
React: props와 state  (0) 2020.10.25
728x90

useCallback특정 함수를 렌더링할 때마다 새로 만들지 않고, 재사용하기 위해 사용하는 Hook이다. 

 

아래 코드에는 useCallback이 사용된 change와 insert라는 함수가 있다.

change의존성 배열이 빈 배열이고, insert의존성배열에 string과 stringList가 들어가 있다. 

 

const change = useCallback((e) => {
	setString(e.target.value); 
}, []); 

const insert = useCallback((e) => {
	const newList = stringList.slice(); 
    	newList.push(string); 
    	setStringList(newList); 
}, [string, stringList]); 

 

둘의 차이가 무엇이길래, 하나는 의존성 배열이 비어있고, 하나는 값이 포함되어 있을까?

 

change는 두번째 인자에 빈 배열을 주었기 때문에, 최초 렌더링 시에만 함수가 생성되고 이후에는 생성되지 않는다. 

 

insertstring과 stringList가 변경될 때만 함수를 재생성한다. 

 

즉, insert의 경우 string과 stringList가 변하면 함수도 재생성되어야 한다. 즉, 두 state에 의존적이다.

하지만, change는 state를 사용하지 않고, 단지 state를 변경하는 것이므로, state에 의존적이지 않다.

state가 변함에 따라 change 함수를 재생성해줄 필요는 없는 것이다.

 

정리하자면, 해당 함수에서 state를 사용한다면, 말그대로 state에 의존하는 함수이므로 두번째 인자의 배열 안에 state를 추가해줘야 하고,

state에 의존적이지 않은 함수라면 빈 배열로 넣어주면 된다.   

 

 

 

 

728x90
728x90

antd form을 사용할 때, 특정 input의 값을 reset하고 싶을 때가 있다. 

 

예를 들어, 아래와 같이 'password'라는 name을 가진 Input을 reset 시키고 싶다면, 

 

        <Form.Item
          name="password"
          label="비밀번호"
          rules={[
            {
              required: true,
              message: '비밀번호를 입력해주세요!',
            },
          ]}
          hasFeedback
        >
          <Input.Password placeholder="비밀번호를 입력해주세요" />
        </Form.Item>

 

다음과 같이 form.setFieldsValue 안에서 password(Form.Item의 name) : ''로 바꿔주면 된다.  

 

form.setFieldsValue({
      password: '',
});
728x90
728x90

activefocususer의 action에 따라 스타일링의 변화를 주고 싶을 때 사용하는 선택자(User Action Pseudo-classes)이다. 

 

둘은 비슷해보이지만, 차이가 있다. 

 

focus는 (input을 눌러) focusing이 되었을 때, background-color를 변하게 하고 싶으면 사용한다. 

 

 

active는 (input을) 누르는 찰나에 background-color를 변하게 하고 싶을 때 사용하면 된다. 

 

728x90
728x90

a 클릭 영역 늘리기

a(anchor)의 기본 display는 inline이다. 그래서 부모의 영역이 넓더라도(예를 들어, 부모의 width가 500px이라고 하자), a태그의 width가 200px이라면, 클릭할 수 있는 영역의 width는 200px 밖에 되지 않는다. 

 

하지만, anchor의 클릭 영역이 작으면 클릭하기가 불편하기 때문에, anchor가 부모의 영역만큼 차지하게 해주는 것이 좋다. 

 

block은 width 값을 따로 부여하지 않을 경우, 부모의 100%만큼 width가 설정된다. (inline-block은 아님)

 

그래서, 다음과 같이 anchor의 display를 block으로 바꾸어주면, a의 클릭 영역을 늘릴 수 있다. 

 

a {
  display: block;
}

 

next에서 router.push()와 Link의 차이 

결론부터 말하자면, SEO(검색엔진최적화)가 중요하다면 Link를 사용하는 게 좋다

 

Link를 사용함으로써, 전체 페이지가 reload 되어 overriden(SSR)된다. 즉, Link를 사용했을 때 검색엔진의 크롤러는 Link가 다른 페이지로 이동하기 위함을 알 수 있고 검색엔진 최적화에 좋다

 

router.push는 주로 이벤트 핸들러(ex. onClick)에서 사용된다고 한다. 그렇지만 단지 다른 페이지로 이동하기 위해서는 사용하지 않는 게 좋다.

 

즉, Link의 기능만으로 부족해서 router의 특정 기능이 필요한 게 아니라면 Link를 사용하는 게 좋다. 

 

참고링크) https://stackoverflow.com/questions/65086108/next-js-link-vs-router-push-vs-a-tag?openLinerExtension=true

 

 

 

 

 

   

728x90
728x90

next에서 antd가 적용이 안 되고 있는 것 같다면, 

모든 페이지에서 공통으로 적용되게 하는 pages 폴더 아래, _app.js 파일에 다음 문장이 import 되어 있는지 확인하자. 

 

import 'antd/dist/antd.css';

 

antd.css를 import해야 antd 적용이 가능하다. 

728x90

'Front-end > React' 카테고리의 다른 글

[React] 클립보드에 텍스트 복사하기  (0) 2021.05.05
Redux가 필요한 이유와 기본 개념  (0) 2020.11.14
React: props와 state  (0) 2020.10.25
728x90

axios.defaults.baseURL

axios 요청을 보낼 때 아래처럼 url을 적어주어야 한다. 그런데 'http://localhost:3065' 부분은 공통이라 계속 적어주어야 한다. 나중에 배포를 하게 되어 url이 바뀌게 되면, 모든 url 부분을 다 바꿔줘야 하는데 굉장히 귀찮은 일이다. 

 

function loginAPI(data) {
  return axios.post('http://localhost:3065/user/login', data);
}

function logoutAPI() {
  return axios.post('http://localhost:3065/user/logout');
}

function signUpAPI(data) {
  return axios.post('http://localhost:3065/user', data);
}

 

이럴 때, axios.defaults를 사용한다. 'axios.defaults'를 통해 모든 요청에 적용될 구성 기본 값을 지정할 수 있다.

 

axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

 

나의 경우는 redux-saga를 사용했기 때문에 saga 폴더의 index.js 파일에서 baseURL을 지정해주었다. 이렇게 해주면, 나중에 배포 후에 baseURL이 변하더라도 이 부분만 변경해주면 된다.

 

axios.defaults.baseURL = 'http://localhost:3065';

 

findOne

로그인한 유저를 db에서 꺼내오고 싶을 때, findOne을 사용해준다. findOne테이블의 데이터를 하나만 가져온다는 뜻이다. 

 

const user = await User.findOne({ where: { id } });
728x90

+ Recent posts