Front-end Developer

0%

논리 연산자

논리연산자 AND와 OR은 비트 연산자 &, |와 비슷하다.

  • &&: 좌항과 우항이 모두 참인가? => 좌항, 우항이 모두 참이면 참, 하나라도 참이 아니면 거짓
  • ||: 좌항 혹은 우항이 하나라도 참인가? => 좌항, 우항 하나라도 참이면 참, 둘 다 참이 아니면 거짓
  • !: 단항 연산자, 우항의 불리언 결과를 반대로 만든다 => 우항이 거짓이면 참, 참이면 거짓이다.

OR(||) 연산자

주의: if문 안의 표현식들이 종종 평가되지 않을 때가 있다.

1
if(1 + 1 == 2) || (1 + 2 == 2)

if(1 + 1 == 2)를 평가하면 이므로 두번 째 표현식 (1 + 2 == 2)는 실행되지 않는다. OR 연산자는 참 | 참 = 참이거나 참 | 거짓 = 참이다. 따라서 앞의 결과가 참이면 뒤의 코드인 1 + 2 == 2는 실행하지 않는다. 즉 표현식 평가를 하지 않는다.

1
if(1 + 2 == 2) || (1 + 1 == 2)

위와 같이 순서를 바꾸었을 때 (1 + 2 == 2)를 평가하면 거짓이다. 위의 경우 OR연산자(||)는 거짓 | 참 = 참 또는 거짓 | 거짓 = 거짓의 조건이 가능하다. 즉 뒤의 코드인 if(1 + 1 == 2)를 평가해야 그 평가 결과에 따라 참 또는 거짓이 될 수 있다. 이 경우에는 뒤의 코드까지 표현식 평가를 진행해야 한다.

AND(&) 연산자

1
if(1 + 1 == 2 && 1 + 2 == 2)

(1 + 1 == 2)는 표현식 평가 결과가 참이다. 하지만 AND연산자(&)는 참 & 참 = 참 또는 참 & 거짓 = 거짓의 조건이 가능한다. 즉 뒤의 코드인 1 + 2 == 2를 평가해야 그 평가 결과에 따라 참 또는 거짓이 될 수 있다. 따라서 뒤의 코드까지 표현식 평가를 진행해야 한다.

1
if(1 + 2 == 2 && 1 + 1 == 2)

1 + 2 == 2의 결과가 거짓이다. AND 연산자는 거짓 & 참 = 거짓이거나 거짓 & 거짓 = 거짓이다. 따라서 앞의 결과가 거짓이면 뒤의 코드인 1 + 1 == 2는 평가하지 않아도 된다.

if문의 다른 표현법

아래 if 문의 (!(a == b || c == d))의 코드를 다른 방식으로 표현하면 (a != b && c != d)이다. 이것이 가능한 이유는 아래와 같다.

!(a == b) !(||) !(c==d) => !(a == b) == (a !== b) => !(||) == && => !(c == d) == (c !== d) => (a != b && c != d)

1
2
3
4
5
6
7
8
9
if(!(a == b || c == d))
{
//do something
}

if(a != b && c != d)
{
//do something
}

if/else if/if 문과 논리

불필요한 연산을 피한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
string scoreString = Console.ReadLine();
int score = int.Parse(scoreString);

if(score >= 90)
{
Console.WriteLine("hello1");
}
if(score < 90 && score >= 80)
{
Console.WriteLine("hello2");
}
if(score < 80 && score >= 70)
{
Console.WriteLine("hello3");
}

위의 코드는 아래와 같은 형태로 바꿀 수 있다. 첫 번째 if문의 조건 score >= 90를 통과했다면 이미 90점 이상은 아닌, 미만이라는 뜻이다. 따라서 두번째 평가식에서 score < 90 && score >= 80와 같은 코드를 짤 필요가 없다. 참고로 Console.ReadLine();은 아래와 같이 변수 할당하지 않고 사용해도 된다.

1
2
3
4
5
string scoreString = Console.ReadLine();
int score = int.Parse(scoreString);

// score에서 바로 사용한다.
int score = int.Parse(Console.ReadLine());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
string scoreString = Console.ReadLine();
int score = int.Parse(scoreString);

if(score >= 90)
{
Console.WriteLine("hello1");
}
if(score >= 80)
{
Console.WriteLine("hello2");
}
if(score >= 70)
{
Console.WriteLine("hello3");
}

아래의 코드도 else if가 불필요하게 사용되었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
string scoreString = Console.ReadLine();
int score = int.Parse(scoreString);

if( score >= 90 )
{
Console.WriteLine("hello1");
}
else if (score < 90 && score >= 80)
{
Console.WriteLine("hello2");
}
else if (score < 80 && score >= 70)
{
Console.WriteLine("hello3");
}

아래의 코드는 완전히 잘못 구현한 조건식이다. score에 두 번째 조건식에 해당하는 80보다 크거나 같은 숫자, 세번 째 조건식에 해당하는 90보다 크거나 같은 숫자가 입력되면 두 숫자 모두 score >= 70의 조건을 만족하기 때문에 첫 번째 조건문에서 평가가 끝나버린다. 따라서 실행되고자 원했던 예상하는 if 조건문에 도달하지 못하게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
string scoreString = Console.ReadLine();
int score = int.Parse(scoreString);

if(score >= 70)
{
Console.WriteLine("hello1");
}
if(score >= 80)
{
Console.WriteLine("hello2");
}
if(score >= 90)
{
Console.WriteLine("hello3");
}

조건 연산자

(불리언 표현식) ? 반환값1(불리언 표현식이 참) : 반환값2(불리언 표현식이 거짓)

  • 조건 연산자를 다른 말로 삼항 연산자라 한다.
  • ?!의 두 가지 기호를 사용한다.
  • 불리언 표현식을 평가하여 참과 거짓일 때 서로 다른 반환값을 반환한다.
  • 매우 간단한 비교를 할 때는 if/else 구문보다 훨씬 빠르다.
  • 남용하면 가독성이 떨어지기 때문에 삼항 연산자 안에 삼항 연산자를 사용하는 식의 구조로 사용하지 않도록 한다.

연산자 우선순위

  • 수학과 마찬가지로 대부분 연산자 결합 순서는 왼쪽에서 오른쪽으로 결합한다.(결합법칙)
  • 소수 연산자만이 오른쪽에서 왼쪽으로 대입한다. e.g. a = b = c + d 는 c + d부터 더한 후 b에 대입하고, 그것을 최종적으로 a에 대입한다.
  • 증감 연산자는 곱셈에 우선한다.
  • *, / 는 +, - 에 우선한다.
1
2
x = ++a * d; // x = (++a) * d
x = a++ * d; // x = (a++) * d

주의 : 우선순위와 평가 순서는 다르다.

1
if (1 + 1 == 2 || 3 + 2 == 5 && 1 + 2 == 2)
  • 우선순위는 &&||보다 높다.
  • 하지만 3 + 2 == 5, 1 + 2 == 2는 실행조차 되지 않는다.
  • ||&& 연산자는 왼쪽에 있는 표현식의 평가를 강제하기 때문이다.

References
실무 프로그래밍 입문(C#)

조건문

  • 조건이 만족하면 if문 아래에 있는 중괄호 코드를 실행한다.
  • 조건은 x와 같은 변수의 값에 따라서 참이나 거짓을 판별할 수 있는 식이나 문장을 의미.
  • 조건식은 참이나 거짓을 반환해야 한다. 이러한 조건식을 불리언 표현식라고 말한다.
1
2
3
4
if(조건식)
{
조건이 만족하면 실행하는 코드
}

구문과 표현식의 관계

  • 구문: 한 줄 이상의 코드로 실행되는 집합. (구문 안에는 한 개 이상의 표현식이 포함될 수 있다.)
  • 표현식: 평가되며 void를 포함하여 값을 반환한다. (단, 일부 표현식은 단독으로 사용할 수 없다.)

관계(비교) 연산자

  • == : 좌우항의 값이 같은가? => 같으면 참, 다르면 거짓 => 반환값은 true
  • != : 좌우항의 값이 다른가? => 같으면 거짓, 다르면 참 => 반환값은 false
  • < : 좌항의 값이 더 작은가? => 작으면 참, 크거나 같으면 거짓 => 반환값은 true
  • <= : 좌항의 값이 작거나 같은가? => 작거나 같으면 참, 크면 거짓 => 반환값은 true
    • 주의 : =<는 존재하지 않는다. 사용 시 에러 발생.
  • > : 좌항의 값이 더 큰가? => 크면 참, 작거나 같으면 거짓 => 반환값은 false
  • >= : 좌항의 값이 크거나 같은가? => 크거나 같으면 참, 작으면 거짓 => 반환값은 true
    • 주의 : =>는 존재하지 않는다. 사용 시 에러 발생.

if/else 문

if 문의 조건식이 참이 아닐 때 수행한다.

1
2
3
4
5
6
7
8
if(조건식)
{
조건이 만족할 때만 실행하는 코드
}
else
{
조건이 만족하지 않을 때만 실행하는 코드
}

if/else if 문

1
2
3
4
5
6
7
8
9
10
11
12
if(조건식 A)
{
조건식 A가 참일 때만 실행
}
else if(조건식 B)
{
조건식 A가 거짓이고, 조건식 B가 참일 때만 실행
}
else if(조건식 C)
{
조건식 A와 B가 거짓이고, 조건식 C가 참일 때만 실행
}

if/else if문 조건식을 만들 때 주의사항

조건문의 순서가 올바른지, 논리적으로 말이 되는지 확인한다. 아래 예시에서 score가 95라고 가정해보았을 때, 첫번 째 만나는 if문의 조건 (score >= 70)을 만족하기 때문에 아래 있는 else if문은 돌지 않고 그대로 코드가 종료된다. 순서를 올바르게 하려면 (score >= 90)이라는 조건이 가장 먼저 와야한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void Main(strings[] args)
{
int score = int.Parse(Console.ReadLine());

if (score >= 70)
{
Console.WriteLine("first");
}
else if (score >= 80)
{
Console.WriteLine("second");
}
else if (score >= 90)
{
Console.WriteLine("third");
}
}

if/else if/else 문

1
2
3
4
5
6
7
8
9
10
11
12
if(조건식 A)
{
조건식 A를 만족할 때만 실행하는 코드
}
else if(조건식 B)
{
조건식 A를 만족하지 않고, 조건식 B를 만족할 때만 실행하는 코드
}
else
{
조건식 A, B를 모두 만족하지 않을 때만 실행하는 코드
}

코딩 표준

  • 공통된 규칙인 코딩 표준이 있어야 코드를 읽기 편하고, 코드 속에 있는 문제점을 발견하기 쉽다.
  • 중괄호를 항상 사용한다: if 문이 한 줄이면 중괄호를 생략할 수 있다. 하지만 이렇게 작성시 예상치 못한 에러를 만들 수 있기 때문에 사용을 지양하도록 한다.
1
if (score >= 90 ) Console.WriteLine("hi");

만약 아래와 같이 코드를 수정하고, score >= 90 일 때만 hi, bye 두 글자가 출력되도록 의도했다고 가정한다. 만약 score에 70이 입력되었다면 score >= 90이라는 주어진 if문의 조건을 만족하지 못하기 때문에 hi, bye라는 글자가 출력될 수 없다. if 문이 중괄호를 생략할 수 있 수 있는 경우는 if 문의 코드가 한 줄일 때만 해당하기 때문에 score가 70점인데도 bye라는 글자가 출력되게 된다.

1
2
3
4
5
6
7
8
9
10
if (score >= 90 )
Console.WriteLine("hi");
Console.WriteLine("bye");

//score가 70점 일 때 아래와 같이 인식되기 때문에 bye가 출력되는 것이다.
if (score >= 90 )
{
Console.WriteLine("hi");
}
Console.WriteLine("bye");
  • 세미콜론이나 중괄호로 실행 단위를 결정한다: 줄 바꿈으로 범위를 제어하려고 하지 않는다.
    • 중괄호의 위치는 정해져 있지 않다. 아래 예시의 두 가지 모두 옳은 방법이다. 하지만 2️⃣번보다는 1️⃣번이 가독성이 좋으므로 이렇게 쓰는 것이 추천된다.
    • Visual Studio에서 ctrl + K + D를 누르면 새로운 줄에서 중괄호가 시작되는 방식으로 자동으로 포맷팅해준다.
1
2
3
4
5
6
7
8
9
10
//1️⃣
if (score >= 90 )
{
Console.WriteLine("hi");
}

//2️⃣
if (score >= 90 ) {
Console.WriteLine("hi");
}

References
실무 프로그래밍 입문(C#)

문자열

  • 여러 개의 문자가 모인 집합체
  • 큰따옴표(")로 감싼다.
  • 기본 자료형(컴퓨터가 바로 이해할 수 있는 자료형)이 아니다.
  • 문자열은 문자형(char)의 배열이다.
  • 문자열끼리의 더하기가 가능하다. 두 문자열을 합쳐 새로운 문자열을 만드는 방식으로 가능하다.
  • 문자열과 숫자도 더하기가 가능하다. 역시 새로운 문자열을 만드는 방식으로 가능하다. e.g. “hello” + “123” => “hello123”
  • 더하기 외의 빼기, 나누기 곱하기는 할 수 없다.
  • ==으로 같은 문자열인지 확인할 수 있다.
  • 큰따옴표만 출력하려면 역슬래시(\) 또는 한국 키보드일 경우 원화(₩)기호를 사용해서 나타낸다. e.g. Console.WriteLine(“\“”)
  • 적은 문자 그대로 출력하고 싶을 때 @기호를 사용한다. e.g. Console.WriteLine(@”\x61”) // \x61 출력
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;

namespace StringOperator
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("hello" + "world");
// Helloworld

string message1 = "hello";
string message2 = "hello";

Console.WriteLine(message1 == message2) //true
}
}
}

문자열 포맷팅

  • +연산자: 기호를 이용해서 문자열을 만들 수 있다.
    • 문자열 + 문자열 e.g. “hello” + “world” => “helloworld”
    • 정수형 + 문자열, 문자열 + 정수형 e.g.”hello” + 100=> hello100
    • 부동소수점형 + 문자열, 문자열 + 부동소수점 e.g.”hello” + 77.7 => hello77.7
    • 결합해야 할 문자열이 길어질수록 가독성이 떨어지고, 임시 문자열이 발생하면서 성능 저하가 발생함.
  • 인덱싱 사용: string.Format 또는 중괄호를 사용하여 포맷팅
    • 소괄호 안에 있는 데이터를 특정 서식에 맞춰 결합
    • 소괄호 안에 있는 데이터를 서식에 맞춰 문자열로 바꿈.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;

namespace StringOperator
{
class Program
{
static void Main(string[] args)
{
string name = 'Lulu';
int id = 210525;

// 방법1
// {0}, {1}과 같은 인덱싱으로 원하는 값을 대입.
string message1 = string.Format("student name: {0}/ student id: {1}", name, id);
Console.WriteLine(message1)

// 방법2
Console.WriteLine("student name: {0}/ student id: {1}", name, id)

// 0을 두 군데에 쓴다면?
Console.WriteLine("hello! {0} student name: {0}/ student id: {1}", name, id)
// {0}, {1}은 뒤에 이어지는 매개변수. 따라서 0번째에 해당하는 매개변수 name, 1번째에 해당하는 매개변수 id가 작성된 곳에 출력된다.
// hello! Lule student name: Lulu/ student id: {1}
}
}
}
  • 문자열 보간: $ 사인을 이용하여 뒤에 따라오는 매개변수 없이도 사용할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System;

namespace StringOperator
{
class Program
{
static void Main(string[] args)
{
string name = 'Lulu';
int id = 210525;

// 방법1
string message1 = string.Format($"student name: {name}/ student id: {id}");
Console.WriteLine(message1)

// 방법2
Console.WriteLine($"student name: {name}/ student id: {id}")
}
}
}

문자열 정렬

  • 스페이스 바 사용: 띄어쓰기를 표현할 수 있으나 수정사항이 발생할 때 유지보수가 힘들어진다.
  • 스페이스 바를 사용하지 않으면 보다 간결하게 표현가능. 유지보수가 용이하다.

    {인덱스, 정렬 길이} (Console.WriteLine, string.Format()에서 동일하게 사용 가능)

    • 기본은 우측정렬
    • 좌측정렬은 음수로 표현. e.g. {0, -6}{1} “Lulu”, “Teemo” (Lulu가 왼쪽부터 6칸을 쓰겠다는 의미.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
static void Main(string[] args)
{
string studentName1 = "kim Leon";
float winRate1 = 20.2351f;

string studentName2 = "Lulu";
float winRate2 = 70.11f;

//0번째 매개변수 studentName1은 10칸, 2번째 매개변수 winRate1,2는 15칸을 차지하도록 설정되었다.
Console.WriteLine("{0,10}{1,15}\n", "Name", "Win Rate");
Console.WriteLine("{0,10}{1,15}\n", "studentName1", "winRate1");
Console.WriteLine("{0,10}{1,15}\n", "studentName1", "winRate2");
Console.WriteLine("{0,10}{1,15}\n", "studentName1", "winRate3");
}

소수점 한자리까지만 출력

{인덱스:f소수점 이하 자릿수}

  • 소수점 이하 자릿수를 {f4}와 같이 표현하여 출력할 자릿수를 정한다.
  • 매개변수의 소수점 이하 자릿수보다 출력해야할 자릿수가 큰 경우 마지막에 0을 붙인다. e.g. “{0:f2}”, 10.1 => 10.10
  • string.Format()에서 동일하게 사용 가능
  • 정수형 가능
  • 반올림 된다.
  • F, f 둘 다 사용가능.
1
2
3
4
5
6
7
8
9
10
static void Main(string[] args)
{
Console.WriteLine("{0}", 3.14159265359); //3.14159265359
Console.WriteLine("{0:f4}", 3.14159265359); //3.1416
Console.WriteLine("{0:f3}", 3.14159265359); //3.142
Console.WriteLine("{0:f2}", 3.14159265359); //3.14
Console.WriteLine("{0:f1}", 3.14159265359); //3.1

Console.WriteLine("{0:f2}", 10.1); //10.10
}

소수점 이하 자릿수 제어하기

{인덱스:f소수점 이하 자릿수}

  • string.Format()에서 동일하게 사용 가능
  • 정수형 가능
  • 반올림 된다.
  • F, f 둘 다 사용가능.
  • 데이터를 바꾸는 것이 아니라 소수점 이하 자릿수를 제어하여 출력하는 것 뿐이다.

10진수를 16진수로 출력하기

{인덱스:x자릿수}

  • string.Format()에서 동일하게 사용 가능
  • 정수형만 가능
  • 소문자 x => 소문자, 대문자 X => 대문자로 출력된다.
1
2
3
4
5
6
7
8
9
10
static void Main(string[] args)
{
Console.WriteLine("{0}", 10); //10
Console.WriteLine("{0}:x", 10); //a
Console.WriteLine("{0}:X", 10); //A
Console.WriteLine("{0}:x1", 10); //a
Console.WriteLine("{0}:x2", 10); //0a
Console.WriteLine("{0}:x3", 10); //00a
Console.WriteLine("{0}:x4", 10); //000a
}

키보드 입력으로부터 숫자 읽어오기

Console.ReadLine();

  • 명령 프롬프트에서 한 줄의 글을 읽는 기능. string name = Console.ReadLine()처럼 사용하면 Console.ReadLine()을 통해 키보드로부터 받은 입력을 문자열 변수인 name에 대입하는 것이다.
  • 엔터키가 입력되기 전까지의 값을 반환.
  • 키보드로부터 받은 값은 반드시 문자열형으로 반환. 문자열은 문자, 숫자도 모두 담을 수 있는 포괄적인 형태이기 때문이다.
  • 문자열을 묵시적/명시적 정수형으로 변환할 수 없다.
    • int num1 = Console.ReadLine();처럼 사용하면 에러 발생. (CS0029. 암시적으로 string 형식을 int 형식으로 변환할 수 없다.)
    • int num2 = (int)Console.ReadLine(); 처럼 사용해도 에러 발생. 타입캐스팅은 서로 호환될 수 있는 숫자끼리 가능하다.(e.g. double => int) 객체 지향 프로그램을 보면 꼭 숫자끼리 가능한 것은 아니지만 현 시점에서 문자와 숫자간에 명시적 변환은 가능하지 않다고 간주하도록 한다. (CS0030. string 형식을 int 형식으로 변환할 수 없다.)
  • Parse()를 통해 문자열을 정수형으로 변환할 수 있다.(e.g. int.Parse();)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    using System;

    namespace AddNumbers
    {
    class Program
    {
    static void Main(string[] args)
    {
    string numStr1 = Console.ReadLine();
    int num1 = int.Parse(numStr1);
    }
    }
    }

References
실무 프로그래밍 입문(C#)

묵시적 변환

  • 기본 자료형 간의 변환을 컴파일러가 알아서 해준다.(모든 기본 자료형 간의 변환이 가능한 것은 아니다.)
  • int/long -> float 또는 long -> double로 변환할 때는 손실이 있을 수 있다. (정밀도의 문제가 있다.)

승격(promotion)

변환과 승격은 엄밀히 따지면 다른 것이지만 프로그래머가 프로그램을 짤 때 이 둘을 구분하는 것은 그렇게 중요하지 않다. 중요한 것은 어떤 것에서 어떤 것으로 변환이 되는지, 변환 시 문제가 있는지, 변환 시 2진수 표현이 바뀌는지 아닌지 등의 여부를 아는 것이 중요하다.

  • 컴파일러가 자동으로 실수형이나 부동소수형 자료의 이진 표현을 확장한다.
  • 산술 연산자 또는 논리 연산자가 제대로 동작하게 하거나 ALU(산술논리장치)가 더 효율적으로 돌 수 있게 하려고 사용한다.
  • 예를 들어 int와 double을 더했을 때 정수+실수의 조건인데, double로 해야 소수점 표현이 가능하니까 두 가지의 합은 double로 출력된다.
  • 작은 형에서 큰 형으로 정수를 변환할 때 문제가 있는가? 없다.
  • 큰 형에서 작은 형으로 변환할 때 문제가 있는가?
    • 값이 충분히 작으면 문제가 없지만 실행 중에 예상했던 값이 아닌 다른 값이 들어갈 수 있고, 만약 실행 중에 값이 바껴서 값이 충분히 작지 않다면 컴파일 에러가 날 수 있다.
    • 위에서 말한 것처럼 런타임 중에 값이 어떨게 될지 모른다는 상황을 정보의 손실이라 한다.

명시적 변환

승격이나 묵시적 변환과 같은 변환 시 발생하는 컴파일 에러같은 문제를 해결하려면 명시적 형변환을 사용하면 된다.

  • 명시적 형변환은 소괄호()를 사용하여 변환하고자 하는 자료형을 보여준다.
  • 명시적 형변환은 프로그래머의 의도를 보여준다는 점에서 중요하다.
  • e.g. num1, num2는 double로 정의되어 있는데 정수형으로 명시적 형변환을 한다. -> int result = (int)num1 + (int)num2;
  • 위의 예시와 같은 형변환시 소수점은 무조건 내림이다.
  • 모든 자료형이 변환되는 것은 아니다.

연산자

  • + 연산자: 두 피연산자를 더하기
  • - 연산자: 두 피연산자를 빼기
    • 연산의 결과가 음수일 경우 조심해야 한다.
    • 부호 없는 피연산자끼리 빼서 음수가 나오면 예상치 못한 값이 나올 수 있으니 조심해야 한다. (e.g. 언더플로우)
  • * 연산자: 두 피연산자를 곱하기, *사용.
  • \/ 연산자: 두 피연산자를 나누기, /사용
    • 정수형의 피연산자는 결과가 제대로 나오지 않을 수 있다.
      -
      1
      2
      3
      4
      int number1 = 10;
      int number2 = 30;
      int result1 = number1 / number2; //0
      double result2 = number1 / number2; //0
    • 위의 예시에서 number1 / number2는 0.3333…의 결과가 나오는데, int형으로 나타내면 소수점 아래는 모두 버리기 때문에 결과가 0이 된다. 그런데 이 결과는 double에 담으면 0.333…이 결과가 될 것 이라고 생각하지만 이 역시도 결과가 0이다. 그 이유는 number1 / number2를 모두 int라고 생각해서 이 연산 결과 자체를 int로 처리한다. 그래서 int 0을 double에 대입한 셈이 되기 때문에 결과는 0이 된다.
    • 만약 result2처럼 원치 않는 결과를 얻기 싫으면 명시적 형변환이 필요하다. e.g. double result3 = (double)number1 / (double)number2; 와 같이 명시적 형변환을 해주면 원래 예상했던 값인 0.333…을 얻을 수 있다.
  • \% 연산자: 나눗셈의 나머지를 구한다. (/연산자는 나눗셈의 몫을 구한다.)

증가/감소 연산자

++ 연산자

  • 피연산자 하나의 값을 1증가
  • 연산기호를 변수의 앞이나 뒤에 붙일 수 있다.
  • num++ 또는 ++num으로 사용.
  • ++이 어디 위치하느냐에 따라 값이 달라진다. 대입과 증가하는 순서가 다르다.
  • 전위 증가 연산자는 증가 후 대입이 이루어지고, 후위 증가 연산자는 대입 후 증가가 이루어진다.

    | 전위 증가 연산자 | 후위 증가 연산자 |
    | ———————— | ———————— |
    | int num = 10; | int num = 10; |
    | ++num; //11 | num++; //10 |
    | num; //11 | num; //11 |

—연산자

  • 피연산자의 값을 1감소

++ 또는 — 연산자를 부동소수점형에 쓴다면 어떻게 될까?

부동소수점형도 증감연산자를 사용할 수 있는데, 예상한 결과를 얻지 못할 수 있으므로 추천되지 않는다. 예를 들어 3.14라는 값을 1증가시켜 4.13로 만들기 위해서 ++연산자를 사용했다고 하면, 기대하던 값과 달리 3.24 또는 3.15와 같은 값이 나올 수 있다. 따라서 웬만하면 증감연산자는 정수형(char, int, long)에 사용하는 것이 좋다.


진법

  • 10진법: 0~9
    e.g. 1024 = 1 * 103 + 0 * 102 + 2 * 101 + 4 *100
  • 2진법: 0,1
    e.g. 1101 = 1 * 23 + 1 * 22 + 0 * 21 + 1 * 20
  • 8진법: 0~7
    e.g. 1703 = 1 * 83 + 7 * 82 + 0 * 81 + 3 * 80
  • 16진법: 0~9, a~f
    • 0~9 다음 a~f는 10~15이다.
      e.g. 1a2 = 1 * 162 + 10 * 161 + 4

진법 변환

바꾸고자 하는 진법의 수를 몫이 0이 될때까지 나눈다. 몫이 0이 된 수의 나머지부터 차례대로 숫자로 표기한다.
e.g. 십진수 3을 2진수로 바꾸고 싶으면, 3을 바꾸고자 2로 나눈다. 10을 2진수로 표기하면 11이다.

어떤 숫자를 각 진법별로 나타내고 싶을 때는 8진수로 먼저 변환한 후 2진수나 16진수로 바꾸는 것이 가장 빠르게 변환하는 방법이다.

비트가 상태를 나타내는데 비트가 1개만 있을 때는 21이고, 2가지 상태를 나타낼 수 있다.
8진수 단위 숫자 하나로 0~7까지 8가지를 표현할 수 있고, 2진수 단위 숫자가 3개가 필요하다.(23 = 8)
2진수에서 4개의 단위 숫자는 16진수 한 개의 단위 숫자로 표현할 수 있다.

  • 21 = 2
  • 22 = 4
  • 23 = 8
  • 24 = 16

e.g. 2778 -> 0101111112 -> fb16

비트 연산자

이진수 피연산자를 비트 단위로 연산할 때 사용
&(and), |(or), ^(xor), ~(not)

1
2
3
4
5
6
7
int op1 = 12; // 0000 0000 0000 0000 0000 0000 0000 1100
int op2 = 9; // 0000 0000 0000 0000 0000 0000 0000 1001

int andResult = op1 & op2; //8(0000 ... 1000)
int orResult = op1 | op2; //13(0000 ... 1101)
int xorResult = op1 ^ op2; //5(0000 ... 1101)
int notResult = ~op1; // -13(1111 ... 0011)

&(and)

숫자끼리 비교할 때 둘 중 하나라도 0이 있으면 최종 결과는 0이 된다. 둘 다 1일 경우는 1이 된다.

op1 1 1 0 0
op2 1 0 0 1
AND 1 0 0 0

|(or)

숫자끼리 비교할 때 둘 중 하나라도 1이 있으면 최종 결과는 1이 된다. 둘 다 0일 경우는 0이 된다.

op1 1 1 0 0
op2 1 0 0 1
OR 1 1 0 1

^(xor)

숫자끼리 비교할 때 두 숫자가 같을 경우는 0이 되고, 다를 경우 1이 된다.

op1 1 1 0 0
op2 1 0 0 1
XOR 0 1 0 1

~(not)

해당하는 숫자를 뒤집는다. 1이면 0, 0이면 1이 된다.

op1 0 1 1 0 0
NOT 1 0 0 1 1

비트 이동 연산자

비트를 왼쪽 혹은 오른쪽으로 움직이는 연산자들
우항으로 부동소수점 수는 사용 불가 (e.g. int result = result1 << 2.5f;)

<<(left-shift): 왼쪽으로 n칸씩 이동한다. 이동하고 나서 남는 숫자는 버리고, 빈 칸은 0으로 채운다. (자릿수를 이동할 때마다 2씩 곱해짐 e.g.int op1 = 12; -> op1 << 1;(24) -> op1 << 2;(48))
>>(right-shift): 오른쪽으로 n칸씩 이동한다. 이동하고 나서 남는 숫자는 버리고, 앞 비트는 0으로 채운다. (자릿수를 이동할 때마다 2씩 나눠짐)

op1 0 0 0 0 1 1 0 0
op1 << 1 0 0 0 1 1 0 0 0
op1 << 2 0 0 1 1 0 0 0 0
op1 >> 3 0 0 0 0 0 0 0 1
op1 >> 4 0 0 0 0 0 0 0 0
1
2
3
4
5
6
7
int op1 = 12; //0000 ... 0000 1100

int leftShiftResult1 = op1 << 1; //24 (0000 ... 0001 1000)
int leftShiftResult2 = op1 << 2; // 48 (0000 ... 0011 0000)

int rightShiftResult1 = op1 >> 3; //1 (0000 ... 0000 0001)
int rightShiftResult2 = op1 >> 4; //0 (0000 ... 0000 0000)

대입 연산자

좌항에 우항의 값을 저장한다. 기호는 =. 수학의 대입처럼 같다는 의미로 사용되는 것이 아니다.

산술 연산자/비트 연산자와 조합

1
2
3
4
5
6
7
8
9
int num1 = 10;

num1 /= 2; // num1 = num1 / 2; 와 깉음.
num1 += 14; // num1 = num1 + 2; 와 깉음.
num1 *= 2; // num1 = num1 * 2; 와 깉음.
num1 -= 20; // num1 = num1 - 2; 와 깉음.

num1 <<=2; // num1 = num1 << 2; 와 같음.
num1 >>=3; // num1 = num1 >> 3; 와 같음.

References
실무 프로그래밍 입문(C#)

변수

  • 변할 수 있는 값
  • 숫자, 문자, 문자열 등을 사용할 수 있다.
  • 문자는 작은따옴표(‘’), 문자열은 큰따옴표(“”)를 사용해야 한다.
  • 어딘 가에 저장한 값을 다시 재사용하기 위해서 사용한다. (상태변화를 기억)
  • 변수명은 의미있게 지어야 한다.
    • 명사로 정확하게 어떤 정보를 담는지 알려주는 단어를 사용한다.
    • 여러 명사가 들어갈 경우 카멜케이스를 사용한다.

코딩 표준

변수 상수
단일 명사 이름 명사 소문자 명사 대문자
복수 명사 이름 카멜케이스 (e.g. mathScore) 대문자로 이루어진 스네이크 케이스(e.g. MATH_SCORE)

기본 자료형

자료형 이름 자료형 크기
byte 8bit 정수
char 16bit 문자
short 16bit 정수
int 32bit 정수
long 64bit 정수
float 32bit 실수
double 64bit 실수

컴퓨터가 이해할 수 있는 가장 자연스러운 형태의 데이터

  • int, float가 가장 많이 쓰이고, 필요에 따라 다른 자료형을 사용한다.
  • 숫자(컴퓨터는 0과 1을 사용해서 표현해야 이해할 수 있다.)
  • 10진수(0~9 10개의 수)
  • 2진수 (0~1 2개의 수)

비트(bit): 2진수의 최소단위. 1 또는 0을 하나만 담을 수 있다. 비트수에 따라 몇 가지 수를 표현할 수 있느냐를 알 수 있다. (bit가 한개면 두 가지 숫자, 두 개면 네 가지 숫자 표현 가능)

  • 8bit = 1byte
  • 1024 bit = 1KB
  • 1024KB = 1MB

정수형

  • 0,1,2와 같은 정수(자연수)를 담을 수 있는 자료형
  • C#에서 각 자료형의 크기(프로그래밍 언어마다 크기는 다름)
    | 자료형 이름 | 자료형 크기 |표현할 수 있는 숫자|
    | ———— | ————— |———|
    | byte | 8bit |28(256개)|
    | short | 16bit |216(65,536개)|
    |int|32bit|232(4,294,967,296개)|
    |long|64bit|264(18,446,744,073,709,551,616개)|

부동소수점형

  • 3.14와 같은 실수를 담을 수 있는 자료형
  • C#에서 각 자료형의 크기(프로그래밍 언어마다 크기는 다름)
    | 자료형 이름 | 자료형 크기 |
    | ———— | ————— |
    |float|32bit|
    |double|64bit|

실수인데 실수형이라고 부르지 않고 부동소수점형이라 부르는 이유는?
컴퓨터에서 숫자를 표현할 때 0.01을 1 * 10-2라고 표현하는데 지수의 숫자에 따라 0.01과 같은 숫자의 소수점이 움직이게 된다. 그래서 고정되지 않고 움직인다는 의미에서 부동소수점형이라 부른다.

문자형

  • 하나의 문자를 담는 자료형
  • 알파벳, 숫자, 특수문자 등
  • 문자는 작은 따옴표(‘)를 사용한다. e.g. ‘a’
  • 자료형 이름은 char이고, 16bit이다.
  • C#에서 char는 유니코드이고, C/C++은 ASCII코드(8bit)이다.

불리언형

  • 정수형이라고 볼 수도 있다.(C++과 같은 일부 언어는 false를 0, ture를 0이 아닌 값으로 표현할 수 있다. 하지만 C#에서는 불가능하다.)
  • 참(ture)/거짓(false)을 표현한다.
  • bool

기본 자료형으로 선언한 변수

1
2
3
4
5
6
7
8
int int1 = 1;

//(뒤에 붙는 f는 float형의 값을 대입할 때 사용. 3.14를 의미)
float float1 = 3.14f;
//(뒤에 f 안붙음.)
double double1 = 5.245;

char char1 = 'a';

각 자료형마다 비트 수가 다른 이유는? byte와 int는 어떤 차이가 있을까?

컴퓨터처럼 생각해본다. 컴퓨터는 2진수로 생각한다. 10진수는 0~9까지 담을 수 있다. 9 다음 오는 숫자인 10의 일의 자리 숫자는 무엇인가? 0이다. 받아올림되었기 때문이다. 2진수도 마찬가지다. 2진수는 더 이상 올라갈 수 없을 때 10진수처럼 옆으로 한자리씩 올라간다. 이 자릿수 하나하나가 비트다.

2비트수

한 개의 비트로는 2개의 수(0,1)을 표현할 수 있고, 두 개의 비트로는 4개(0~3), 세개의 비트로는 8개(0~7)개가 표현 가능하다


음수 표현법

7 0
최상위 비트 최하위 비트

1byte는 8bit. byte는 총 256개의 숫자를 표현할 수 있다. 음수, 양수를 반반으로 배분하면 좋은데 0 때문에 값이 정확하게 반반이 될 수 없다. 그래서 범위를 정한다. 0을 기준으로 양수는 [1, 127], 음수는 [-128, -1]로 정해서 적용한다. 그런데 비트가 하나씩 증가할 때마다 표현 가능한 수는 두 배씩 증가한다. 여기서는 반으로 나누겠다고 했고 반은 두배의 반대개념이다. 그리고 127을 이진수로 나타내면 아래와 같다.

7 0
최상위 비트 최하위 비트
양수 0 1 1 1 1 1 1 1
음수 1 0 0 0 0 0 0 0

이미 7비트를 양수가 다 가져갔기 때문에 그 다음 음수를 쓰려면 bit 첫 글자를 1로 쓸 수 밖에 없다. 최상위 비트를 이용하여 이 값이 0이면 양수, 1이면 음수

  • 양수: 0000 0001 ~ 0111 1111
  • 0: 0000 0000
  • 음수: 1000 0000 ~ 1111 1111

  • 부호 있는 자료형: 음수, 양수를 모두 표현(sbyte, short, int, long)
  • 부호 없는 자료형: 양수만 표현, 보통 부호 없는 자료형 이름 앞에 u를 붙인다.(byte, ushort, uint, ulong)
    • 문자를 담는 8bit 문자형은 기본적으로 unsigned가 말이 된다. 따라서 C#에서 byte는 unsigned, sbyte는 음수, 양수를 모두 담는 signed를 의미한다.
부호 있는 자료형 부호 없는 자료형
음수, 양수를 모두 표현 양수만 표현 (보통 u가 자료형 앞에 붙음)
자료형 sbyte (-128 ~ 127) byte (0 ~ 255)
short (-32,768 ~ 23,767) ushort (0 ~ 65,535)
int (-2,147,483,648 ~ 2,147,483,647 ) uint (0 ~ 4,294,967,295)
long (-9,223,372,036,854,775,808 ~ 9,233,372,036,854,775,807) ulong (0 ~ 18,446,744,073,709,551,615)

부호 있는, 또는 부호 없는 변수는 언제쓰는가?

프로그래머의 명백한 의도를 보여줄 때

예를 들어 int age = 17;과 같은 변수가 있을 때, 부호 있는 자료형 int를 썼다. 프로그램상 int를 사용해서 나이를 처리하는 코드를 만들었는데, 상식적으로 나이는 음수가 될 수 없지만 프로그램 상 처리과정에서 음수가 들어온다면 버그가 발생하는 등 문제가 있을 수 있다. 이럴 때 음수값이 들어오지 않도록 예외처리를 해줄 수 있지만 uint age = 17;과 같이 음수를 받을 수 없는 자료형을 사용하면 음수가 들어올 수 없도록 처리할 수 있다.

uint(0 ~ 4,294,967,295)의 범위를 넘어서는 코드는 어떤 값을 출력할까?

1
2
3
4
5
uint num1 = 4294967295;
uint num2 = 1;
unit result = num1 + num2;

Console.WriteLiner(result);

위의 코드에서 result는 0이 나오게 된다. num1의 숫자 4294967295는 부호없는 정수의 최대값이므로 이를 2진수로 나타내면 1111이 연속된 형태의 숫자이고, 여기에 1을 더하는 것이므로 결과는 10000000...과 같은 형태가 나오게 된다. 그런데 unit는 32bit이고, 이미 num1이 32bit의 최대값이었으므로 여기에 1을 더하면 33bit가 된다. 하지만 unit의 최대 저장공간이 32bit였기 때문에 33bit가 되는 최상위비트 1은 버려지게 된다. 그래서 0만 남게 되어서 결과값이 0이 출력되는 것이다. 이와 같은 현상을 오버플로우라 한다.

오버플로우

  • 위의 예시처럼 연산의 결과로 자료형보다 큰 수가 나온다.
  • 자료형의 크기는 변하지 않기 때문에 자료형의 범위를 넘어간 비트는 버리게 된다.
  • 오버플로우가 발생하면 위의 예시에서 0이라는 결과가 출력된 것처럼 원치않는 값을 얻을 수 있다.
  • 언더플로우: 오버플로우와 반대개념이다. 예를 들어 최소값(최하위비트) 0에서 -1로 가면 uint일 경우 표현할 수 없으니까 최대값으로 표현되는 현상이 발생한다.

변수의 역할에 알맞은 자료형을 선택해야 한다.
오버플로우를 고려해서 내가 쓰고 있는 자료형이 데이터를 충분히 담을 수 있는지 고려해야 한다.

상수의 접두사와 접미사

상수의 앞이나 뒤에 상수를 꾸미는 기호

상수 접두사

상수 앞에 붙는 기호로 진법을 나타낸다.

  • 2진수: 0b- e.g. int num1 = 0b10;
  • 16진수: 0x- e.g. int num2 = 0x10;

상수 접미사

상수 뒤에 붙는 기호로 상수의 형을 나타낸다. 대문자로 써도 되지만 거의 안쓰고, 소문자형으로 나타낸다.

  • 부호 여부: -u e.g. int num1 = 10; uint num2 = 10u; uint num3 = 0x10u;
  • long: -l e.g. int num1 = 1l;(컴파일 오류), long num2 = 10l; long num3 = 10lu;(컴파일 오류), ulong num4 = 10lu;
  • float: -f e.g. float num1 = 10.0f; float num2 = 10.0(컴파일 오류)
  • double: -d. 거의 쓰지않음. e.g. double num1 = 10.0; double num2 = 10.0d;

References
실무 프로그래밍 입문(C#)

1
2
3
4
5
6
7
8
9
10
11
12
13
using system;

namespace HelloWorld
{
class Program
{
// 메인 함수
static void Main(string[] args)
{
Console.WriteLine("Hello world!");
}
}
}

메인 함수

  • 시작점(entry point)
  • c# 프로그램은 반드시 어떤 함수에서부터 실행되어야 한다.
  • 어떤 함수가 바로 Main함수이다. (또는 메서드라고 표현한다.)
  • exe 파일을 실행하면 Main 함수가 자동으로 실행된다.

Main 함수 앞의 static

  • c#은 OOP 언어라서 기본적으로 static을 안쓰지만 Main 함수는 진입점, 프로그램에 딱 하나만 있는 것. 따라서 프로그램의 진입점은 딱 하나만 있기 때문에 static을 넣어서 정적이라고 표현.
  • 전역 함수가 된다.

string[] args

  • 메인 함수가 외부로부터 받는 데이터
  • 함수 인자 또는 메서드 인자라고 부른다.
  • 커맨드 라인으로부터 인자를 받는다.
    • 커맨드 라인: exe 파일을 실행할 때 추가적으로 넣는 정보
    • e.g. HelloWorld.exe Hi C# is fun 는 Hi, C#, is, fun 총 네 개의 인자가 들어간다.
    • 이 인자들이 args 배열에 저장된다.

void

  • 반환형
  • 모든 함수는 반환형이 존재한다.
  • 실제로 값을 반환할 수도 있고, 반환하지 않을 수도 있다.
  • 함수가 어떤 형태의 데이터를 반환하는지.
  • 반환값이 없다는 의미.

반환형의 역할

  • 커맨드 라인이 반환형을 받아서 exe 프로그램이 올바르게 실행되었는지 여부를 알 수 있다.
    • 0: 성공을 의미
    • 0이 아닌 값: 오류 코드

메인 메서드의 정수값 반환

  • 정수를 반환할 때 int를 사용한다.
  • 실제로 값을 반환할 때는 return 키워드를 사용한다.
1
2
3
4
static int Main(string[] args)
{
return 0; //0을 돌려보낸다.
}

using System;

  • using은 c#에서 지시어라고 부른다.
  • 이것을 없애고 빌드하면 오류가 발생한다.
  • using 지시어 다음에는 사용할 라이브러리(Console.WriteLine처럼 함수를 모아 놓은 것) 이름을 넣는다.
  • 프로그램에서 사용할 함수들이 들어있는 네임스페이스를 코드 맨 위에 using 지시어를 사용해서 컴퓨터에게 알린다.

컴파일

  • 소스코드를 기계 또는 VM이 이해할 수 있는 언어로 변경하는 행위
  • 컴파일러라는 프로그램을 사용(커맨드 라인, IDE)
  • 오류 또는 경고가 발생할 수 있다.

컴파일 오류

  • 작성한 코드가 프로그래밍 언어의 규격에 위반된다.
  • 오류가 있으면 exe 파일이 생성되지 않는다.
  • 프로그램 실행 중 문제를 발견하는 것보다 낫다
  • 오류 목록 창에서 오류를 파악하기 어려울 때는 출력창에서 찾아도 된다.
  • 경고는 프로그래머의 실수처럼 보이는 것을 컴파일러가 찾아준다.

디버그 빌드, 릴리즈 빌드

디버그 빌드

  • 개발자가 개발 중에 사용하는 실행파일
  • 디버깅에 유용한 정보가 많다.
  • 최적화는 거의 되어 있지 않다.

릴리즈 빌드

  • 디버깅을 위한 정보가 적다.
  • 최적화가 잘 되어 있다.
  • 성능이 디버깅 모드보다 훨씬 빠르다.

여러값을 출력하기

화면에 글자를 출력할 때 사용하는 함수

  • Console.WriteLine : 줄바꿈
  • Console.Write : 같은 줄

구문

  • 한 줄짜리 코드: 보통 ; 가 코드 끝에 있다.
  • 블록: 여러 줄의 코드로 이루어졌다.
  • 블록은 범위라고도 부르고, 코드를 ({}) 로 감싼다.
  • 주석: 코드와 관련된 내용을 메모할 때 사용, 컴파일할 때 무시되는 문장.
    • 코드와 마찬가지로 계속 관리해야한다. (바뀐 내용이 있다면 업데이트 필요)
    • 너무 많이 쓰지 않도록 한다. 코드 자체가 그 자체로 이해되도록 작성할 것.

상수

  • 절대 변하지 않는 값, 매직 넘버.
  • 숫자, 문자 등을 포함
  • int 형, double형, float형, bool형, char형

선언과 대입(정의)

  • 선언: 변수, 상수의 자료형과 이름을 컴퓨터에게 알려줌
  • 대입: 변수, 상수의 실제 값을 컴퓨터에게 알려줌.
  • 선언과 대입은 동시에 할 수 있다.
  • const 변수는 선언과 대입이 항상 동시에 일어난다.
    • 한번 값을 대입 한 후 값을 바꾸고 싶지 않을 때 사용한다.
    • 선언과 대입이 이루어진 후 값을 변경하면 컴파일 오류가 발생한다.
1
2
3
4
5
6
7
8
9
10
// 선언
float number;
//대입
number = 1.5;

//선언과 대입을 동시에
float number = 1.5;

//const를 쓰는 경우
const float NUMBER = 1.5;

References
실무 프로그래밍 입문(C#)

고수준 언어, 저수준 언어가 고급, 저급의 개념이 아니다. 저수준 언어, 고수준 언어의 정의는 상대적이다.

저수준 언어

  • 컴퓨터가 이해하기 쉬운 언어에 가깝다.
  • 0과 1로만 이루어졌다.
  • 기계어
  • 컴퓨터가 바로 이해할 수 있으므로 변환 과정이 필요 없다.

기계어

  • 네이티브 코드라고도 부른다.
  • 2진수, 0과 1
  • 원조 저수준 언어
  • 모든 프로그래밍 언어는 이 기계어로 번역되어야 한다.

고수준 언어

  • 인간이 이해하기 쉬운 언어에 가깝다.
  • 인간이 사용하는 언어로 이루어졌다.(for, while, if…)
  • 어셈블리어, c, c++, java, c#
  • 컴퓨터가 이해하는 기계어로 변환하는 과정이 필요하다.

어셈블리어

  • 사람이 읽기에 조금 더 편하기 때문에 엄밀히 말하면 고수준 언어이다.
  • 대부분 기계어랑 일대일로 대응한다. 그렇기 때문에 저수준 언어라고 하기도 한다.

고수준 프로그래밍 언어

어셈블리어는 기계어보다는 사람에게 친숙하지만 여전히 불편하다. 그래서 보다 사람에게 친근한 고수준 프로그래밍 언어가 나왔다. e.g. int addResult = 3 + 4; 3과 4를 더한 후 addResult에 저장한다.

  • 사람이 쓰는 말에 매우 가까워졌다.
  • 어셈블리어처럼 기계와어 일대일 대응은 불가능하다.
  • c, c++, java, c#, javascript 등.
  • 메모리를 누가 관리하는지에 따라 매니지드 언어, 언매니지드 언어로 나뉜다.

컴파일 언어

  • 소스 코드 -> 컴파일러 -> 기계어 -> 실행
  • 소스 코드를 기계가 이해할 수 있는 기계어로 바꿔준다.
  • c, c++
  • 오프라인 컴파일러가 고수준 언어를 네이티브 코드로 컴파일
  • 다양한 최적화 가능
  • 플랫폼(e.g. mac OS, 32비트 윈도우, 아이폰)마다 다른 exe 파일을 컴파일 해야한다.

JIT 컴파일 언어

  • 컴파일 언어와 인터프리터 언어의 중간단계
  • 소스코드 -> 컴파일러 -> 중간언어 코드(기계처럼 보이게 프로그램을 짜고, 그 프로그램이 이해하는 코드) -> JIT 컴파일러 -> 기계어 -> 실행
  • c#, java, WASM(webAssembly)
  • 오프라인 컴파일러가 소스 코드를 중간 언어로 컴파일
  • 플랫폼 별로 VM(가상 머신, vitual machine)를 설치해야 함.
  • 프로그램 실행 중 VM이 중간 언어를 기계가 이해하는 언어로 바꿔서 실행.
  • java, c#은 플랫폼에 종속적이지 않고 VM에 종송적이다.

인터프리터 언어

  • 소스 코드 -> 인터프리터 언어 -> 기계어 -> 실행
  • 컴파일 단계가 없다
  • javascript, python, lua, php
  • 실행 중에 고수준 언어를 네이티브 코드로 바꿔서 실행한다.
  • 실제로 실행하기 전까지 코드의 문제를 잡아낼 수 없다.

강한 타입, 약한 타입 언어

변수선언(변수 용도)와 관련이 있다.

  • 약한 타입: 변수형을 선언한 후 변수형을 변경할 수 있다 e.g. javascript
  • 강한 타입: 변수형을 선언한 후 변수형을 변경할 수 없다 e.g. c, c++, c#(약한타입도 지원), java 등

상용 코드를 작성할 때는 강한 타입 언어들이 일반적으로 더 낫다.


매니지드 언어, 언매니지드 언어

  • 누가 메모리의 수명(라이프사이클)을 관리하는지가 핵심
  • 프로그램에서 큰 데이터를 사용하려면 운영체제(OS)로부터 독점적으로 사용할 메모리를 확보해야 한다.
  • 데이터를 다 사용한 뒤에는 OS에 메모리는 반환해야 한다.

매니지드 언어: 프로그래머가 책임지고 메모리를 반환한다.
언매니지드 언어: 프로그래밍 언어가 더 이상 메모리를 사용 안 하는 상황을 판단하여 메모리를 반환해 준다.

매니지드 언어가 실수가 적고 편하지만 효율적이지는 않다. 언매니지드 언어를 배우면 하드웨어 지식을 쌓을 수 있다.

성능이 중요하지 않은 프로그램을 만들 때
매니지드 언어를 많이 사용한다. e.g. java, c#, javascript, lua

성능이 중요한 프로그램을 만들 때
언매니지드 언어를 많이 사용한다. e.g. c, c++

언매니지드 언어를 배워야 하는 이유

  • 메모리 동작원리
  • CPU 동작원리
  • 컴퓨터처럼 생각하는 방법
  • 매니지드 언어에서 지원하는 모든 마법같은 기능들의 동작원리

프로그래밍 패러다임 - 절차적 언어

  • 가장 단순한 형태의 프로그래밍 언어
  • 함수를 실행하면 결과가 바로 반횐된다.
  • 그 결과를 저장하고 싶으면 별도의 공간에 보관한다
  • 기능과 자료저장의 분리
  • 순서에 따라 코드를 실행한다.
  • 대표적인 언어: c언어

프로그래밍 패러다임 - OOP 언어

  • 사람에게 직관적
  • 기능과 자료를 하나의 개체(object)에 합쳤다.
    • 물체의 개념
    • 오역으로 인해 주로 객체하고 부른다.
  • 가장 널리 쓰이는 프로그래밍 패러다임
    • 사람에게 매우 직관적이다.
    • 복잡한 프로그램의 구조를 잡기 쉽다.

프로그래밍 패러다임 - 함수형 언어

  • 절차적 언어랑 비슷하게 함수가 있다.
  • 그러나 상태의 변화가 없다(불변성)

C

  • 강한 타입 언어
  • 매니지드 언어
  • OOP

References
실무 프로그래밍 입문(C#)

pseudo-class

가상 클래스 또는 의사 클래스라고 한다.

1
2
3
4
/* form */
selector:pseudo-class {
property: value;
}

선택자에 추가하는 키워드. 선택한 요소가 특별한 상태를 만족할 때 변화를 주기 위해 사용한다. 예를 들어 element:hover를 사용하면 element에 마우스 포인터가 올라갈 때만 특정 변화가 발생한다. 아래와 같이 사용하면 버튼 요소에 사용자가 마우스를 올렸을 때만 글자 색상이 파란색으로 변경된다.

1
2
3
button:hover {
color: blue;
}

자주 쓰이는 pseudo-class

  1. LVHA
    (:link — :visited — :hover — :active)의 순서를 말하는데, :active, :visited, :focus는 실제 스타일링 할 때 보다는 예시로 자주 사용된다. 주의할 점은 :active는 자신보다 뒤에 위치하고, 동등한 명시성을 가진 (:link, :hover, :visited)를 덮어쓰기 때문에 가장 마지막에 배치되어야 한다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    a:link {
    color: blue;
    } /* 방문하지 않은 링크 */
    a:visited {
    color: purple;
    } /* 방문한 링크 */
    a:hover {
    background: yellow;
    } /* 마우스를 올린 링크 */
    a:active {
    color: red;
    } /* 활성화한 링크 */

    p:active {
    background: #eee;
    } /* 활성화한 문단 */
    • :active
      사용자가 활성화한 요소를 나타낸다. 마우스를 사용할 때, 활성이란 마우스 버튼을 누르는 순간부터 떼는 시점까지를 의미한다. 보통 \,\과 함께 사용한다.
    • :hover
      사용자가 포인팅 장치를 사용하여 상호작용 중인 요소를 선택한다. :active가 마우스를 클릭하는 동안을 의미한다면 :hover는 마우스 포인터가 특정 요소에 올라가 있는 동안을 의미한다. 보통 사용자의 마우스 포인터(커서)가 요소 위에 올라가 있을 때 선택된다. 단, :hover는 터치스크린에서 활성화하지 않거나 터치한 직후에 또는 터치 이후 다른 요소를 터치하기 전까지 계속 활성화 할 수도 있어서 문제가 많다. 이를 주의하여 제한적으로 hover 효과를 주거나 hover가 불가능한 장치에서도 콘텐츠 접근에 문제가 없도록 개발해야 한다.
  2. 형제 요소 선택

    • :first-child
      형제 요소 중 첫 요소. 초기에는 부모가 있는 요소만 선택가능했으나 Selectors Level 4부터 제한이 사라졌다. 아래 예시에서 css를 다음과 같이 작성하면 원래 아무것도 선택되지 않았다. \ 태그는 부모 요소가 아니기 때문이다. 그러나 지금은 선택이 가능하다.
    • :nth-child
      형제 사이에서의 순서에 따라 요소를 선택한다. nth-child(1)는 형제 요소 중 첫 번째 요소를 선택하는 것이다.
    • :last-child
      형제 요소 중 가장 마지막 요소. \ 태그가 여러 개 있다고 가정할 때, 가장 마지막 \에게만 margin-bottom: 10px;을 주고 싶다면 div:last-child:의 형태로 사용한다. 반대로 모든 \에 동일하게 margin-bottom: 10px;을 주되, 가장 마지막 \에는 별도의 margin을 주고 싶지 않다면 div:not:last-child와 같이 사용할 수 있다.
1
2
3
<div />
<p>This text is selected!</p>
<p>This text isn't selected.</p>
1
2
3
4
5
6
7
div p:first-child {
color: red;
}

div p:nth-child(1) {
color: red;
}

pseudo-element

의사 요소, 또는 가상 요소라 한다.

1
2
3
4
/* form */
selector::pseudo-element {
property: value;
}

선택자에 추가하는 키워드. 선택한 요소의 일부분에만 스타일을 입힐 수 있다. 이때 하나의 선택자에는 하나의 의사 요소만 사용 가능하다. 예를 들어 ::first-child를 사용하면 문단 첫 줄의 글씨체만 바꿀 수 있다.

1
2
3
4
5
/* The first line of every <p> element. */
p::first-line {
color: blue;
text-transform: uppercase;
}

pseudo-class vs pseudo-element

  1. 규칙
    의사 클래스는 (:)를 사용하고, 의사 요소는 (::)를 사용한다. 의사 클래스는 특정 상태에 스타일을 적용할 때 사용하고, 의사 요소는 특정 부분에 스타일을 적용할 때 사용한다. 즉 의사 클래스는 사용자가 마우스를 클릭하거나 마우스 커서를 특정 요소 위에 올려두었을 때의 특정 상태에 이르렀을 때 변화를 주기 위해 사용하고, 의사 요소는 아래와 같이 특정 요소의 앞이나 뒤에 장식 요소를 추가할 수 있다. 보통 content와 함께 사용된다.
1
2
3
4
5
6
7
8
9
/* a 태그 뒤에 삽입 */
a::after {
content: '♥';
}

/* a 태그 뒤에 삽입 */
a::before {
content: '♥';
}
  1. 우선 순위
    같은 요소가 여러 선언의 대상일 때 어떤 것이 우선 적용되는지를 결정하는 우선 순위 점수에서 가상클래스가 가상요소보다 순위가 높다. 우선순위 점수는 다음과 같다.

    1. !important: 이를 사용하면 다른 모든 요소를 무시하고 가장 최우선으로 적용된다.
    2. 명시도: 선언된 선택자의 명시도를 우선순위에 따라 계산한다.
      1. inline styles
      2. IDs
      3. classes, pseudo-class, attribute
      4. elements, pseudo-element
    3. 선언 순서 : 명시도 점수가 같다면, 가장 마지막에 선언된 것이 우선순위를 가진다.

References
pseudo-class
pseudo-element

Images on the Web

이미지는 웹 페이지이에서 50%의 바이트를 차지하는데, 보통 페이지가 로드될 때 표시되는 가장 큰 요소인 경우가 많기 때문에 Largest Contentful Paint에 영향을 미친다. 모든 이미지의 절반은 그 사이즈가 1MB를 초과하기 때문에 웹에 디스플레이 될 때 최적화되지 않는다. 요즘에는 사용자들이 웹을 모바일, 태블릿, 컴퓨터 등 다양한 방식으로 웹을 탐색하는데 이미지는 아직도 one size로 고정되어 있다. 예를 들어 웹 사이트가 2000 x 2000 픽셀의 이미지를 로드한다고 하면, 모바일에서는 100 x 100 픽셀로만 표시한다. 더욱이 웹 페이지 이미지의 30%는 초기에 뷰포트 외부에 존재한다. 이는 브라우저가 유저가 스크롤을 내려서 아래에 있는 내용을 보기 전에도 브라우저가 이미지를 로드한다는 것이다.

웹페이지에서 이미지를 성능을 최적화하는 방식으로 사용하려면 size, weight, lazy loading, and modern image formats 등 고려해야 할 요소가 많다. 개발자는 이미지를 최적화하기 위해서 복잡한 빌드 도구를 설정해야 하는데 이러한 도구들이 일반적으로 외부 데이터 소스로부터 가져온 사용자가 제출한 이미지를 다루지는 않기 때문에 모든 이미지를 최적화할 수는 없다. 이는 필연적으로 사용자 경험을 저해시킨다.

Next.js Image Component

가장 기본적인 Next.js 이미지 컴포넌트는 모던 웹을 위해 진화한 HTML의 \ 요소의 대체물이다.

1
2
3
4
5
6
<img
src="/profile-picture.jpg"
width="400"
height="400"
alt="Profile Picture"
/>
1
2
3
import Image from 'next/image'

<Image src="/profile-picture.jpg" width="400" height="400" alt="Profile Picture">

next/image 컴포넌트를 사용하면 이미지가 자동으로 lazy-load되고, 이는 유저가 이미지를 실제로 보는 상황에 가까워졌을 때 비로소 렌더링됨을 의미한다. 이렇게 하면 초기 뷰포트 외부에 있는 이미지의 30%가 로딩되는 것을 방지할 수 있다. 이미지 사이즈는 강제적으로 적용되어, 브라우저가 로드될 당시에 이미지에 필요한 공간을 건너뛰어 레이아웃이 이동되는 현상을 막고 즉시 렌더링할 수 있다.

widthheight는 HTML의 \ 요소에서는 반응형 레이아웃에서 문제가 발생할 수 있다. 하지만 next/image를 쓰면 제공된 widthheight의 비율에 따라서 자동으로 반응형 사이즈에 맞출 수 있다. 개발자는 초기 뷰포트에 이미지를 표시하여, Next.js가 자동으로 이 이미지를 사전에 로드하도록 할 수 있다.

Automatic Image Optimization

next/image 컴포넌트는 built-in Image Optimization을 통해 자동으로 가장 작은 사이즈의 이미지를 생성한다. 이는 모든 이미지 소스에서 작동하므로, 외부 데이터 소스로 부터 가져온 CMS와 같은 이미지에도 최적화된다.

사용방법

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import Image from 'next/image';

function Home() {
return (
<>
<h1>My Homepage</h1>
<Image
src='/me.png'
alt='Picture of the author'
width={500}
height={500}
/>
<p>Welcome to my homepage!</p>
</>
);
}

export default Home;

위와 같이 next/image를 사용하는 것 외에 next.config.js을 통해 Image Optimization에 대한 보다 advanced use case를 적용할 수도 있다.

Domains

외부 웹사이트에서 호스팅되는 이미지에 대해 죄적화를 사용하려면 이미지에 절대경로 url을 src로 사용하고, 어떤 도메인의 최적화를 허용할 것인지 설정한다. 이는 외부 url이 남용되지 않도록 하기 위해 필요한 것이다.

1
2
3
4
5
module.exports = {
images: {
domains: ['example.com'],
},
};

Loader

Next js의 빌트인 이미지 최적화 기능 대신 클라우드 provider를 이용하여 이미지를 최적화하려면 로더와 path를 구성할 수 있다. 이는 이미지 src에 상대경로 url을 사용하여 provider가 올바른 절대 경로 url을 구성할 수 있도록 돕는다.

1
2
3
4
5
6
module.exports = {
images: {
loader: 'imgix',
path: 'https://example.com/myaccount/',
},
};

아래와 같은 클라우드 provider가 제공된다.

  • Vercel: Works automatically when you deploy on Vercel, no configuration necessary. Learn more
  • Imgix: loader: ‘imgix’
  • Cloudinary: loader: ‘cloudinary’
  • Akamai: loader: ‘akamai’
  • Default: Works automatically with next dev, next start, or a custom server

Advanced

Device Sizes

웹 사이트 사용자로부터 예상되는 장치의 width를 알고 있는 경우에 deviceSizes 프로퍼티를 사용하여 특정 장치 사이즈의 breackpoint를 지정할 수 있다. 이 width는 next/image 컴포넌트가 layout="responsive" 또는 layout="fill"을 사용할 때나 장치에 따른 올바른 이미지가 제공된다. 만약 따로 deviceSizes 프로퍼티를 지정하지 않으면 아래 값이 default로 사용된다.

1
2
3
4
5
module.exports = {
images: {
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
},
};

Image Sizes

imageSizes 프로퍼티를 이용하여 특정 이미지 width 목록을 지정할 수 있다. 이러한 width는 deviceSizes의 배열과 연결되어 있으므로 deviceSizes배열에 정의 된 width와 달라야하고, 일반적으로 그 배열의 width보다 작아야 한다. 이 width는 next/image 컴포넌트가 layout="fixed"또는 layout="intrinsic"를 사용해야한다. 만약 따로 imageSizes 프로퍼티를 지정하지 않으면 아래 값이 default로 사용된다.

1
2
3
4
5
module.exports = {
images: {
imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
},
};

next/image

아래와 같이 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import Image from 'next/image';

function Home() {
return (
<>
<h1>My Homepage</h1>
<Image
src='/me.png'
alt='Picture of the author'
width={500}
height={500}
/>
<p>Welcome to my homepage!</p>
</>
);
}

export default Home;

props

아래와 같은 속성들을 필요로 한다.

  1. src
    : 소스 이미지의 경로 또는 url을 말하며, 필수이다. 외부 url을 사용한다면 반드시 next.config.js에서 domains를 추가해주어야 한다.
  2. width
    : 이미지의 width를 말하며, 픽셀이다. 반드시 단위가 없는 정수이어야 한다. (layout=’fill’)이 아닌 경우에 필요하다.
  3. height
    :이미지의 height를 말하며, 픽셀이다. 반드시 단위가 없는 정수이어야 한다. (layout=’fill’)이 아닌 경우에 필요하다.

optional props

  1. layout
    뷰포트 사이즈가 변화될 때 이미지의 layout behavior이다.
    • intrinsic: 기본값. 이미지는 작은 뷰포트 크기에서는 축소되는데 큰 뷰포트에서는 원래 크기를 유지한다.
    • fixed: 이미지의 크기가 뷰포트의 변화에 따라 변화되어도 이미지 크기가 변경되지 않는다.(반응형 아님) HTML의 img 태그와 유사하다.
    • responsive: 이미지는 작은 뷰포트 크기에서는 축소되고, 큰 뷰포트에서는 확대된다.
    • fill: 이미지가 부모 요소의 width와 height의 크기에 따라 stretch된다. 보통 object-fit과 함께 사용된다.
  2. sizes
    device sizes를 미디어 쿼리로 매핑하는 문자열. 기본값은 device sizes이다.
  3. quality
    최적화 된 이미지의 품질. 1~100 사이의 정수이며, 100이 최고의 품질이다. 기본값은 75이다.
  4. priority
    true이면 이미지가 높은 우선 순위로 간주되며, 미리 로드된다. 단, 이미지가 스크롤 없이 볼 수 있는 부분에만 표시되었을 때 사용해야 한다. 기본값은 false이다.

Advanced Props

  1. objectFit
    layout="fill"을 사용할 때 이미지가 fit된다.
  2. objectPosition
    layout="fill"을 사용할 때 이미지의 위치를 말한다.
  3. loading
    이미지가 로드될 때의 동작을 의미. 기본값은 lazy이다. lazy일 때 뷰포트로부터 계산된 거리에 도달할 때까지 이미지의 로드를 연기한다. eager라면 이미지를 즉시 로드한다.
  4. unoptimized
    true인 경우 소스 이미지의 퀄리티, 사이즈 포맷이 변경 되는 대신 그대로 제공된다. 기본값은 false이다.

Other Props

img 컴포넌트의 다른 요소들은 아래를 제외하고는 기본 요소로 전달된다.

  • style 대신 className을 사용.
  • srcSet 대신 Device Sizes를 사용.
  • decoding은 항상 async이다.

References
Next.js 10
next/image
Image Component and Image Optimization

HTTP(Hypertext Transfer Protocol)

  • HTML 문서와 같은 리소스들을 가져올 수 있도록 해주는 프로토콜(컴퓨터 내부에서, 또는 컴퓨터 사이에서 데이터의 교환 방식을 정의하는 규칙 체계).
  • 웹에서 이루어지는 모든 데이터 교환의 기초
  • 클라이언트-서버 프로토콜: (보통 웹브라우저인) 수신자 측에 의해 요청이 초기화되는 프로토콜

client와 server가 서로 어떻게 커뮤니케이션하는지를 표준화한 TCP/IP 기반의 *어플리케이션 계층 커뮤니케이션 프로토콜이다.content가 어떻게 요청되고, 어떻게 인터넷을 통해 전송되는지를 정의한다.


  • *어플리케이션 계층 프로토콜(application layer protocol): hosts(client와 server)가 어떻게 커뮤니케이션하는지를 표준화한 추상화된 계층이며, TCP/IP가 client와 server사이에 요청과 응답을 주고 받는 것에 의존한다.

HTTP 기반 시스템의 구성요소

각가의 개별적인 요청들은 서버로 전송되고, 서버는 전달받은 요청을 처리하여 response라고 불리는 응답을 제공한다. 그리고 이 요청과 응답 사이에는 다양한 작업을 수행하는 게이트웨이, 캐시 역할을 하는 프록시 등의 여러 개체들이 존재한다. 라우터, 모뎀 등의 개체도 존재하는데 웹은 계층적으로 설계되어 있기 때문에 이들은 네트워크과 전송 계층 내로 숨겨지고, HTTP는 어플리케이션 계층의 최상위에 있다.

클라이언트: 사용자 에이전트

사용자를 대신하여 동작하는 모든 도구, 주로 브라우저에 의해 수행된다. 브라우저는 항상 요청을 보내는 개체이며, 결코 서버가 될 수 없다.

브라우저가 *웹 페이지를 표시하기 위해서는 페이지의 *HTML 문서를 가져오기 위한 요청을 전송한 뒤, 파일을 구문 분석하여 실행해야 할 스크립트와 하위 리소스들(보통 이미지와 비디오)을 잘 표시하기 위한 레이아웃 정보(CSS)에 대응하는 추가적인 요청들을 가져온다. 그리고, 하나의 완전한 문서인 웹페이지를 표시하기 위해 리소스들을 혼합한다. 즉, 하나의 완전한 문서는 텍스트, 레이아웃 설명, 이미지, 비디오, 스크립트 등 불러온(fetched) 하위 문서들로 재구성된다.


  • *웹페이지: 하이퍼텍스트 문서. HTML 언어로 쓰여진 browser(브라우저)를 통해 보여지는 단순한 문서이다. 위에서 설명한 것처럼 웹 페이지는 다양한 다른 종류의 자원을 포함할 수 있다.
  • *HTML(*HyperText Markup Language)</u>: 웹을 이루는 가장 기초적인 구성 요소로 웹 콘텐츠의 의미와 구조를 정의할 때 사용하고 웹 브라우저에 표시되는 글, 이미지 등의 다양한 컨텐츠를 표시하기 위해 마크업을 사용한다. 마크업은 \, \처럼 다양한 요소를 사용한다.</li> <li>*하이퍼텍스트:웹 페이지를 다른 페이지로 연결하는 링크로 웹의 근본적인 속성이다.</li> </ul> <hr> <h3 id="웹-서버"><a href="#웹-서버" class="headerlink" title="웹 서버"></a>웹 서버</h3><p>통신 채널의 반대편에 존재하는 클라이언트에 의한 요청에 대한 문서를 제공하는 서버.</p> <h3 id="프록시"><a href="#프록시" class="headerlink" title="프록시"></a>프록시</h3><p>웹 브라우저와 서버 사이에서는 수많은 컴퓨터와 머신이 HTTP 메시지를 이어 받고 전달하는데,여러 계층으로 이루어진 웹 스택 구조에서 이러한 컴퓨터/머신들은 대부분은 전송, 네트워크 혹은 물리 계층에서 동작하며, <em>성능에 상당히 큰 영향을 주지만 HTTP 계층에서는 이들이 어떻게 동작하는지 눈에 보이지 않는다.</em> <strong>이러한 컴퓨터/머신 중에서도 애플리케이션 계층에서 동작하는 것들을 일반적으로 프록시라고 부른다.</strong> 프록시는 눈에 보이거나 그렇지 않을 수도 있으며(프록시를 통해 요청이 변경되거나 변경되지 않는 경우를 말함) 다양한 기능들을 수행할 수 있다</p> <ul> <li>캐싱 (캐시는 공개 또는 비공개가 될 수 있다 (예: 브라우저 캐시))</li> <li>필터링 (바이러스 백신 스캔, 유해 컨텐츠 차단(자녀 보호) 기능)</li> <li>로드 밸런싱 (여러 서버들이 서로 다른 요청을 처리하도록 허용)</li> <li>인증 (다양한 리소스에 대한 접근 제어)</li> <li>로깅 (이력 정보를 저장)</li> </ul> <hr> <h2 id="HTTP의-기초"><a href="#HTTP의-기초" class="headerlink" title="HTTP의 기초"></a>HTTP의 기초</h2><ul> <li><code>간단하다</code>: HTTP/2가 다소 더 복잡해졌지만 여전히 HTTP 메세지를 프레임별로 캡슐화하여 간결함을 유지하고 있고, <strong>사람이 읽을 수 있도록 간단하게 고안되어 있다.</strong></li> <li><p><code>확장가능하다</code>: HTTP/1.0에서 소개된, *<u>HTTP 헤더</u>는 HTTP를 확장하고 실험하기 쉽게 만들어주었기 때문에, 클라이언트와 서버가 새로운 헤더의 시맨틱에 대해 간단한 합의만 한다면, 언제든지 새로운 기능을 추가할 수 있다.</p> </li> <li><p><code>stateless, but not sessionless</code>: 상태를 저장하지 않는다. 동일한 연결 상에서 연속하여 전달된 두 개의 요청 사이에는 연결고리가 없다. <strong>HTTP의 핵심은 상태가 없는 것이지만 HTTP 쿠키는 상태가 있는 세션을 만들도록 해준다.</strong> 헤더 확장성을 사용하여, 동일한 컨텍스트 또는 동일한 상태를 공유하기 위해 각각의 요청들에 세션을 만들도록 HTTP 쿠키가 추가된다.</p> </li> <li><code>HTTP와 connections</code>: 연결(connection)은 전송 계층에서 제어되므로 근본적으로 HTTP 영역 밖이다. HTTP는 연결될 수 있도록 하는 근본적인 전송 프로토콜을 요구하지 않는다. 다만 그저 신뢰할 수 있거나 메시지 손실이 없는(최소한의 오류는 표시) 연결을 요구할 뿐이다. HTTP는 연결이 필수는 아니지만 연결 기반인 TCP 표준에 의존한다.</li> </ul> <p>클라이언트와 서버가 HTTP를 요청/응답으로 교환하기 전에 여러 왕복이 필요한 프로세스인 TCP 연결을 설정해야 하고, 기본적인 TCP 연결은 Connection 헤더를 사용해 부분적으로 제어할 수 있다.</p> <hr> <ul> <li>*<u>HTTP 헤더</u>: 클라이언트와 서버가 요청 또는 응답으로 부가적인 정보를 전송할 수 있도록 해준다. HTTP 헤더는 대소문자를 구분하지 않는 이름과 콜론 ‘:’ 다음에 오는 값(줄 바꿈 없이)으로 이루어져있다. 값 앞에 붙은 빈 문자열은 무시된다.</li> </ul> <hr> <h2 id="HTTP로-제어할-수-있는-것"><a href="#HTTP로-제어할-수-있는-것" class="headerlink" title="HTTP로 제어할 수 있는 것"></a>HTTP로 제어할 수 있는 것</h2><ol> <li>캐시<br>HTTP로 문서가 캐시되는 방식을 제어할 수 있다. 서버는 캐시 대상과 기간을 프록시와 클라이언트에 지시할 수 있고 클라이언트는 저장된 문서를 무시하라고 중간 캐시 프록시에게 지시할 수 있다.</li> <li>origin 제약사항을 완화하기<br>스누핑과 다른 프라이버시 침해를 막기 위해, 브라우저는 웹 사이트 간의 엄격한 분리를 강제힌다. <strong>동일한 origin으로부터 온 페이지만이 웹 페이지의 전체 정보에 접근할 수 있다.</strong> 그런 제약 사항은 서버에 부담이 되지만, HTTP 헤더를 통해 그것을 완화시킬 수 있다. 그런 덕분에 문서는 다른 도메인으로부터 전달된 정보를 패치워크할 수 있다(그렇게 하려면 어떤 경우에 보안과 관련된 사항이 있을 수도 있다).</li> <li>인증<br><strong>어떤 페이지들은 보호되어 오로지 특정 사용자만이 그것에 접근할 수도 있다</strong>. 기본 인증은 HTTP를 통해 WWW-Authenticate 또는 유사한 헤더를 사용해 제공되거나, HTTP 쿠키를 사용해 특정 세션을 설정하여 이루어질 수도 있다.</li> <li>프록시와 터널링<br><strong>서버 혹은 클라이언트 혹은 그 둘 모두는 종종 인트라넷에 위치하며 다른 개체들에게 그들의 실제 주소를 숨기기도 한다.</strong> HTTP 요청은 네트워크 장벅을 가로지르기 위해 프록시를 통해 나가게 된다. 모든 프록시가 HTTP 프록시는 아니다. 예를 들면 SOCKS 프로토콜은 좀 더 저수준에서 동작한다. FTP와 같은 다른 프로토콜도 이 프록시를 통해 처리될 수 있다.</li> <li>세션<br><strong>쿠키 사용은 서버 상태를 요청과 연결하도록 해준다. 이것은 HTTP가 기본적으로 상태없는 프로토콜임에도 세션을 만들어주는 계기가 된다.</strong> 이것은 e-커머스 쇼핑 바구니를 위해서 유용할 뿐만 아니라 사용자 구성을 허용하는 모든 사이트에 대해서 유용하다.</li> </ol> <hr> <h2 id="HTTP의-흐름"><a href="#HTTP의-흐름" class="headerlink" title="HTTP의 흐름"></a>HTTP의 흐름</h2><ol> <li><code>TCP 연결을 연다</code>: 요청을 보내거나(혹은 여러개의 요청) 응답을 받는데 사용되고, 클라이언트는 새 연결을 열거나, 기존 연결을 재사용하거나, 서버에 대한 여러 TCP 연결을 열 수 있다.</li> <li><code>HTTP 메시지를 전송</code>: HTTP 메시지(HTTP/2 이전의)는 인간이 읽을 수 있다. HTTP/2에서는 이런 간단한 메시지가 프레임 속으로 캡슐화되어, 직접 읽는게 불가능하지만 원칙은 동일하다.</li> </ol> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">GET / HTTP/1.1</span><br><span class="line">Host: developer.mozilla.org</span><br><span class="line">Accept-Language: fr</span><br></pre></td></tr></table></figure> <ol> <li><code>서버에 의해 전송된 응답 읽어들이기</code>:</li> </ol> <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">HTTP/1.1 200 OK</span><br><span class="line">Date: Sat, 09 Oct 2010 14:28:02 GMT</span><br><span class="line">Server: Apache</span><br><span class="line">Last-Modified: Tue, 01 Dec 2009 20:18:22 GMT</span><br><span class="line">ETag: "51142bc1-7449-479b075b2891b"</span><br><span class="line">Accept-Ranges: bytes</span><br><span class="line">Content-Length: 29769</span><br><span class="line">Content-Type: text/html</span><br><span class="line"></span><br><span class="line"><!DOCTYPE html... (here comes the 29769 bytes of the requested web page)</span><br></pre></td></tr></table></figure> <ol> <li><code>연결을 닫거나 다른 요청들을 위해 재사용하기</code></li> </ol> <hr> <h2 id="HTTP-메세지"><a href="#HTTP-메세지" class="headerlink" title="HTTP 메세지"></a>HTTP 메세지</h2><ul> <li>HTTP/1.1과 초기 HTTP 메시지: 사람이 읽을 수 있다.</li> <li>HTTP/2: 이 메시지들은 새로운 이진 구조인 프레임 안으로 임베드되어, 헤더의 압축과 다중화와 같은 최적화를 가능케 한다.</li> <li>HTTP 메시지의 두 가지 타입인 <code>요청(requests)과 응답(responses)</code>은 각자의 형식을 가지고 있다.</li> </ul> <h3 id="요청의-구성요소"><a href="#요청의-구성요소" class="headerlink" title="요청의 구성요소"></a><code>요청</code>의 구성요소</h3><ul> <li><code>HTTP 메서드</code>: <strong>보통 클라이언트가 수행하고자 하는 동작을 정의한 GET, POST 같은 동사나 OPTIONS나 HEAD와 같은 명사.</strong> 일반적으로, 클라이언트는 리소스를 가져오거나(GET을 사용하여) HTML 폼의 데이터를 전송(POST를 사용하여)하려고 하지만, 다른 경우에는 다른 동작이 요구될 수도 있다.</li> <li><code>가져오려는 리소스의 경로</code>: 예를 들면 프로토콜 (http://), 도메인 (developer.mozilla.org), 또는 TCP 포트 (80)인 요소들을 제거한 리소스의 URL.</li> <li><code>HTTP 프로토콜의 버전</code></li> <li><code>서버에 대한 추가 정보를 전달하는 선택적 헤더들</code></li> <li><code>응답의 본문과 유사한 본문</code>: POST와 같은 몇 가지 메서드를 위한, 전송된 리소스를 포함</li> </ul> <h3 id="응답의-구성요소"><a href="#응답의-구성요소" class="headerlink" title="응답의 구성요소"></a><code>응답</code>의 구성요소</h3><ul> <li>HTTP 프로토콜의 버전.</li> <li>요청의 성공 여부와, 그 이유를 나타내는 상태 코드.</li> <li>아무런 영향력이 없는, 상태 코드의 짧은 설명을 나타내는 상태 메시지.</li> <li>요청 헤더와 비슷한, HTTP 헤더들.</li> <li>선택 사항으로, 가져온 리소스가 포함되는 본문.</li> </ul> <hr> <h2 id="HTTP-기반-API"><a href="#HTTP-기반-API" class="headerlink" title="HTTP 기반 API"></a>HTTP 기반 API</h2><p>HTTP 기반으로 가장 일반적으로 사용된 API는 user agent와 서버간에 데이터를 교환하는데 사용될 수 있는 *<u>XMLHttpRequest API</u>이다. 최신 *<u>Fetch API</u>는 보다 강력하고 유연한 기능을 제공합니다.</p> <ul> <li>*<u>XMLHttpRequest(XHR) API</u>: <strong>XHR객체는 서버와 상호작용하기 위해 사용되며, 전체 페이지의 새로고침없이도 URL 로부터 데이터를 받아올 수 있다.</strong> 이는 웹 페이지가 사용자가 하고 있는 것을 방해하지 않으면서 페이지의 일부만 업데이트할 수 있도록 해준다. XMLHttpRequest 는 AJAX 프로그래밍에 주로 사용된다. 또한 XML만 받아올 수 있을 것 같아 보이지만, 모든 종류의 데이터를 받아오는데 사용할 수 있다. 또한 HTTP 이외의 프로토콜도 지원한다(file 과 ftp 포함). 이 XHR</li> <li>* Fetch API: 네트워크 통신을 포함한 리소스 취득을 위한 인터페이스가 정의되어 있다. XMLHttpRequest와 같은 비슷한 API가 존재하지만, Fetch API는 좀더 강력하고 유연한 조작이 가능하다.</li> </ul> <hr> <p><em>References</em><br><a href="https://developer.mozilla.org/ko/docs/Web/HTML" target="_blank" rel="noopener">HTML: Hypertext Markup Language</a><br><a href="https://developer.mozilla.org/ko/docs/Learn/Common_questions/Pages_sites_servers_and_search_engines" target="_blank" rel="noopener">웹페이지, 웹사이트, 웹서버 그리고 검색엔진의 차이는 무엇일까요?</a><br><a href="https://developer.mozilla.org/ko/docs/Glossary/Protocol" target="_blank" rel="noopener">프로토콜</a><br><a href="https://kamranahmed.info/blog/2016/08/13/http-in-depth/" target="_blank" rel="noopener">Journey to HTTP/2</a><br><a href="https://developer.mozilla.org/ko/docs/Web/HTTP/Overview" target="_blank" rel="noopener">HTTP 개요</a><br><a href="https://developer.mozilla.org/ko/docs/Web/HTTP/Headers" target="_blank" rel="noopener">HTTP 헤더</a><br><a href="https://developer.mozilla.org/ko/docs/Web/API/XMLHttpRequest" target="_blank" rel="noopener">XMLHttpRequest</a><br><a href="https://developer.mozilla.org/ko/docs/Web/API/Fetch_API" target="_blank" rel="noopener">Fetch API</a></p> </div> <footer class="post-footer"> <div class="post-eof"></div> </footer> </article> </div> <nav class="pagination"> <a class="extend prev" rel="prev" href="/page/6/"><i class="fa fa-angle-left" aria-label="Previous page"></i></a><a class="page-number" href="/">1</a><span class="space">…</span><a class="page-number" href="/page/6/">6</a><span class="page-number current">7</span><a class="page-number" href="/page/8/">8</a><span class="space">…</span><a class="page-number" href="/page/14/">14</a><a class="extend next" rel="next" href="/page/8/"><i class="fa fa-angle-right" aria-label="Next page"></i></a> </nav> </div> <script> window.addEventListener('tabs:register', () => { let activeClass = CONFIG.comments.activeClass; if (CONFIG.comments.storage) { activeClass = localStorage.getItem('comments_active') || activeClass; } if (activeClass) { let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`); if (activeTab) { activeTab.click(); } } }); if (CONFIG.comments.storage) { window.addEventListener('tabs:click', event => { if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return; let commentClass = event.target.classList[1]; localStorage.setItem('comments_active', commentClass); }); } </script> </div> <div class="toggle sidebar-toggle"> <span class="toggle-line toggle-line-first"></span> <span class="toggle-line toggle-line-middle"></span> <span class="toggle-line toggle-line-last"></span> </div> <aside class="sidebar"> <div class="sidebar-inner"> <ul class="sidebar-nav motion-element"> <li class="sidebar-nav-toc"> Table of Contents </li> <li class="sidebar-nav-overview"> Overview </li> </ul> <!--noindex--> <div class="post-toc-wrap sidebar-panel"> </div> <!--/noindex--> <div class="site-overview-wrap sidebar-panel"> <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person"> <p class="site-author-name" itemprop="name">Heejin Lee</p> <div class="site-description" itemprop="description">Today I Learned</div> </div> <div class="site-state-wrap motion-element"> <nav class="site-state"> <div class="site-state-item site-state-posts"> <a href="/archives/"> <span class="site-state-item-count">133</span> <span class="site-state-item-name">posts</span> </a> </div> <div class="site-state-item site-state-tags"> <span class="site-state-item-count">6</span> <span class="site-state-item-name">tags</span> </div> </nav> </div> </div> </div> </aside> <div id="sidebar-dimmer"></div> </div> </main> <footer class="footer"> <div class="footer-inner"> <div class="copyright"> © <span itemprop="copyrightYear">2022</span> <span class="with-love"> <i class="fa fa-user"></i> </span> <span class="author" itemprop="copyrightHolder">Heejin Lee</span> </div> <div class="powered-by">Powered by <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> v4.2.1 </div> <span class="post-meta-divider">|</span> <div class="theme-info">Theme – <a href="https://muse.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Muse</a> v7.7.1 </div> </div> </footer> </div> <script src="/lib/anime.min.js"></script> <script src="/lib/velocity/velocity.min.js"></script> <script src="/lib/velocity/velocity.ui.min.js"></script> <script src="/js/utils.js"></script> <script src="/js/motion.js"></script> <script src="/js/schemes/muse.js"></script> <script src="/js/next-boot.js"></script> </body> </html>