노마드 코더에서 진행하는 챌린지 기록들
노개북 챌린지 8일차
오늘의 범위
- episode 35: 비밀번호는 어떻게 저장될까?
- episode 36: 객체 지향 프로그래밍이 뭐죠? ①
- episode 37: 객체 지향 프로그래밍이 뭐죠? ②
- episode 38: 함수형 프로그래밍이 뭐죠?
Ep 35 비밀번호는 어떻게 저장될까?
비밀번호를 데이터베이스에 그대로 저장하는 것은 DB에 접근 가능한 개발자, 운영자 모두가 알 수 있어 좋지 못한 방법이다. 데이터베이스 자체를 암호화하여 key를 사용자에게 제공하고 로그인 시 대조하여 푸는 방법도 있지만 키를 잃어버리거나 유출될 가능성 때문에 이것 역시 좋은 방법은 아니다.
그럼 어떻게 비밀번호를 저장해야 좋을까?
비밀번호 저장의 좋은 예
위의 두 가지 방법 대신 해시 함수를 사용하면 된다. 해시 함수는 데이터베이스 내에 존재하여 입력한 값을 무작위 값으로 변환시켜주는 역할을 한다.
해시 함수의 특징
- 동일한 입력값에 동일한 출력값을 가짐
- 입력값이 조금이라도 다르면 출력값은 크게 바뀐다. 즉, 비슷한 입력값이라도 출력값을 유추할 수 없다는 것이다.
- 출력값을 입력한다고 하여 원래 값(=입력값)이 나오지 않는다.
해시 함수의 특징을 이용하면 보다 괜찮은 비밀번호 시스템을 구현할 수 있지만 완벽한 것은 아니다. 해시 함수가 변경한 값을 원래 값과 연결한 표인 레인보우 테이블의 존재 때문이다. 해시 함수를 통과한 값이 레인보우 테이블에 저장되기 때문에 여기에 문제가 발생하면 보안이 위험해질 수 있다.
솔트(salt)
이런 문제점 때문에 솔트라는 것이 등장했다. 솔트는 작은 무작위 텍스트로 입력값에 더하여 해시 함수에 통과시킬 때 사용한다. 패스워드와 솔트를 합치면 레인보우 테이블에서도 검색할 수 없어서 그냥 해시 함수에만 통과시켰을 때 보다 훨씬 안전하다.
Ep 36 객체 지향 프로그래밍이 뭐죠? ①
객체 지향 프로그래밍은 일종의 프로그래밍 패러다임이라고 할 수 있다. 프로그래밍 패러다임은 프로그래밍할 때의 관점, 방식 등을 말한다.
게임 플레이어로 3명의 데이터를 생성한다고 해보자.
const player1 = {
name: '짱구',
health: 85,
skill: '부리부리',
};
const player2 = {
name: '철수',
health: 90,
skill: '영어 스피킹',
};
const player3 = {
name: '유리',
health: 99,
skill: '토끼 폭행',
};
이 플레이어들은 모두 같은 속성을 가졌다. 만약 플레이어가 100명쯤 된다고 하면 어떨까. 한 속성을 추가하기 위해 100번의 코드 수정을 해야 할 것이다. 너무 비효율적이다.
객체 지향 프로그래밍을 지원하는 자바스크립트에는 클래스라는 개념이 존재한다. 아래와 같이 클래스를 사용하여 코드를 개선할 수 있다.
class Player {
constructor(name, health, skill) {
this.name = name;
this.health = health;
this.skill = skill;
this.xp = 0; // 클래스 내부에서 기본 제공도 가능하다.
}
}
const player1 = new Player('짱구', 85, '부리부리');
const player2 = new Player('철수', 90, '영어 스피킹');
const player3 = new Player('유리', 99, '토끼 폭행');
아까보다도 코드가 훨씬 간단해졌다.
Ep 37 객체 지향 프로그래밍이 뭐죠? ②
객체 지향 프로그래밍의 또 다른 주요 특징인 상속의 개념에 대해서도 알아보자. 심즈라는 게임을 구현한다고 가정해볼 때, 인간은 아기, 청소년, 성인으로 크게 분류해볼 수 있다.
class Adult {
constructor(name) {
this.name = name;
this.arms = 2;
this.legs = 2;
}
}
class Teenager {
constructor(name) {
this.name = name;
this.arms = 2;
this.legs = 2;
this.emotional = true; // 감정 속성
}
curse() {
return `tlqkf!`; // 욕설 기능
}
}
class Baby {
constructor(name) {
this.name = name;
this.arms = 2;
this.legs = 2;
this.cute = true; // 귀여움 속성
}
cry() {
return `응애`; // 우는 기능
}
}
위 코드를 보면 Adult, Teenager, Baby 클래스에 공통된 속성이 있다. 이를 간단하게 해결하고 싶을 때 바로 상속을 사용하는 것이다. 공통된 속성만을 가지고 있는 Adult 클래스를 나머지 두 클래스에 상속시키면 중복을 해결할 수 있다.
class Adult {
constructor(name) {
this.name = name;
this.arms = 2;
this.legs = 2;
}
}
class Teenager extends Adult {
constructor(name) {
this.emotional = true; // 감정 속성
}
curse() {
return `tlqkf!`; // 욕설 기능
}
}
class Baby extends Adult {
constructor(name) {
this.cute = true; // 귀여움 속성
}
cry() {
return `응애`; // 우는 기능
}
}
상속을 사용하니 코드의 길이가 확연히 줄어든 게 보인다. 의미도 명확해졌다. 이러한 특성들만 보더라도 왜 객체 지향 프로그래밍이 유행이었는지를 알 수 있을 것이다.
Ep 38 함수형 프로그래밍이 뭐죠?
함수형 프로그래밍도 객체 지향 프로그래밍처럼 프로그래밍 패러다임 중 하나이다. 버그가 발생하기 어려운 구조이기 때문에 배워두는 것이 좋다.
명령형 프로그래밍과 선언형 프로그래밍
먼저 명령형 프로그래밍과 선언형 프로그래밍을 살펴보자. 명령형 프로그래밍은 원하는 결괏값에 도달하는 방법을 알려주고 선언형 프로그래밍은 원하는 결괏값을 선언한다는 것에서 차이가 있다.
텍스트에서 공백을 ☆로 바꾸는 코드를 작성한다고 하면, 명령형 프로그래밍은 아래와 같이 텍스트의 공백을 하나씩 교체하는 방법에 대해 알려준다.
function spaceToStar(text) {
let result = '';
for (let i = 0; i < text.length; i++) {
text[i] === ' ' ? (result += '☆') : (result += text[i]);
}
return result;
}
text를 한 글자씩 비교하면서 공백이면 별로 대체하고 아니면 본래 글자를 이어 붙이는 방식으로 코드를 작성한다.
반면에 선언형 프로그래밍은,
function spaceToStar(text) {
return text.replaceAll(' ', '☆');
}
replaceAll
을 사용하여 텍스트 공백을 별로 대체하도록 코드를 작성한다. replaceAll
은 명령형 프로그래밍으로 작성되어 있을 텐데, 실제 어떤 작업을 하는지는 선언형 프로그래밍에서 중요하지 않다.
정리하면, 명령형 프로그래밍은 자세한 지시를 내릴 수 있지만 실수하기 쉽고 동료가 이해하기 어렵다는 특징이 있지만 선언형 프로그래밍은 결과 중심으로 코드를 작성하니까 실수 가능성도 적을뿐더러 동료의 이해도 쉬운 편이라는 것이다.
함수형 프로그래밍
함수형 프로그래밍은 선언형 프로그래밍을 유지하면서 함수 중심으로 코드를 작성하는 것이다. 아래 items에서 홀수를 제거하는 예시를 보면,
function checkForOdd(item) {
return item % 2 === 0;
}
function removeOdd(items) {
return items.filter(checkForOdd);
}
removeOdd
함수 내부에 checkForOdd
라는 또 다른 함수를 인자로 받아들였다. 함수형 프로그래밍은 이러한 방식으로 자주 쓰인다.
느낀 점
패스워드를 암호화한다는 것은 알고 있었지만 어떤 식으로 저장하는지는 몰랐는데 해시 함수랑 솔트를 사용하는 방법이 있다는 것을 알게 됐다.
- 입력값 + 솔트 => 해시 함수 => 출력값 = 안전한 비밀번호 생성!
References
book - IT 5분 잡학사전