튜토리얼

Controller

@RestController를 추가해주면서 Getmapping등의 Mapping 주소를 받는다.

 

Test Code

private MockMvc mvc
//웹 API를 테스트할때 사용

mvc.perform(get("/hello")) // -> hello주소로 http get을 요청함

.andExpect(status().isOk()) // -> 결과의 상태를 검증함

.andExpect(content().string(hello)) //-> hello랑 콘텐츠의 결과값이 같은지 확인

 

Dto

생성자들을 처리해줌

@Getter

@Getter
@requiredArgsConstructor
public class HelloresponseDto{
	private final String name;
    private final int amount
}

 

TestCode

HelloResponseDto dto = new HelloResponseDto(name,amount);

assertThat(dto.getName()).isEqualTo(name) //테스트 검증 라이브러리

 

Controller에서도 Dto 사용하기

@GetMapping("/hello/dto")
public HelloResponseDto helloDto(@RequestParam("name") String name, @RequestParam("amount") int amount){
	return new HelloResponseDto(name, amount);
}

 

Dto 사용한 controller 테스트

String name = "hello";
int amount = 100;

mvc.perform(
			get("/hello/dto")
            	.param("name", name) //인자값 넣어주기
                .param("amount", String.valueOf(amount)))
                
            .andExpect(status().isOk))
            .andExpect(jsonPath("$.name", is(name)))
            .andExpect(jsonPath("$.amount", is(amount))); //jsonPath는 JSON 응답값을 필드별로 검증
            //$를 기준으로 필드명 명시

 

게시판 만들기

 

domain 폴더는 게시글, 댓글, 회원, 정산, 결제 등 소프트웨어에 대한 요구사항 혹은 문제 영역

 

Posts클래스

@Getter
@NoArgsConstructor
@Entity // -> 테이블과 링크될 클래스임을 나타냅니다
public class Posts extends BaseTimeEntity {
    @Id // -> PK인 것을 나타냅니다.
    @GeneratedValue(strategy = GenerationType.IDENTITY) // -> auto_increment 추가
    private Long id; //-> 왠만하면 Long으로 하기

    @Column(length = 500, nullable = false) //-> 선언안해도 되는데, 추가되는 옵션 있으면 필수
    private String title;

    @Column(columnDefinition = "TEXT", nullable = false)
    private String content;

    private String author;

    @Builder //-> 생성자 대신에 하는 것
    public Posts(String title, String content, String author){
        this.title = title;
        this.content = content;
        this.author = author;
    }

    public void update(String title, String content){
        this.title = title;
        this.content = content;
    }

}

 

PostRepository -> DB Layer 접근자.

JpaRepository<Entity 클래스, PK타입>을 상속하면 CRUD 메소드 자동생성

public interface PostsRepository extends JpaRepository<Posts,Long> {
}

 

Test Code

@RunWith(SpringRunner.class)
@SpringBootTest
public class PostsRepositoryTest {
    @Autowired
    PostsRepository postsRepository;

    @After //단위테스트가 끝날때마다 해주는 것 여기서는 초기화!!!
    public void cleanup(){
        postsRepository.deleteAll();
    }

    @Test
    public void 게시글저장_불러오기(){
        //given
        String title = "테스트 게시글";
        String content = "테스트 본문";

        postsRepository.save(Posts.builder() //-> 테이블 posts에 insert/update쿼리 실행
        .title(title)
        .content(content)
        .author("jiwon803@gmail.com")
        .build());

        //when
        List<Posts> postsList = postsRepository.findAll(); // -> 모두 불러옴

        //then
        Posts posts = postsList.get(0);
        assertThat(posts.getTitle()).isEqualTo(title);
        assertThat(posts.getContent()).isEqualTo(content);
    }
}

 

API 생성

API를 만들기 위해서는 총 3개의 클래스가 필요함

  1. Request 데이터를 받을 Dto
  2. API 요청을 받을 Controller
  3. 트랜잭션, 도메인 기능 간의 순서를 보장하는 Service

Spring Web 계층

  • Web Layer
    • 흔히 사용하는 컨트롤러(@Controller)와 JSP/Freemarker 등의 뷰 템플릿 영역
    • 이외에도 필터(@Filter), 인터셉터, 컨트롤러 어드바이스 등 외부 요청과 응답에 대한 전반적인 영역
  • Service Layer
    • @Service에 사용되는 서비스 영역
    • 일반적으로 Controller와 dao의 중간영역
    • @Transactional이 사용되어야 하는 영역
  • Repository Layer
    • Database와 같이 데이터 저장소에 접근하는 영역
    • 기존에 개발하셨던 분들이라면 Dao로 이해하면 편함
  • Dtos
    • Dto(Data Transfer Object)는 계층 간에 데이터 교환을 위한 객체를이야기하면 Dtos는 이들의 영역
    • 뷰 템플릿 엔진에서 사용될 객체나 Repository Layer에서 결과로 넘겨준 객체등
  • Domain Model
    • 개발 대상을 모든 사람이 동일한 관점에서 이해할 수 있고 공유할 수 있도록 단순화시킨 것을 도메인 모델
    • 택시 앱이라고 하면 배차, 탑승 ,요금 등이 도메인
    • @Entity가 사용된 영역 역시 도메인 모델
    • 무조건 데이터 베이스의 테이블과 관계가 있어야되는건 아님

 

104

'개발합시다. > BackEnd 공부' 카테고리의 다른 글

Django의 request & Http  (0) 2021.12.08
Redis 추가공부 사항  (0) 2021.12.08
Redis의 모든 것  (0) 2021.12.07
mongoDB의 모든 것  (0) 2021.12.07
Nginx의 모든 것  (0) 2021.12.07

Redis란?

Redis는 Remote Dictionary Server의 약자로서, 키-값 구조의 비정형 데이터를 저장하고 관리하기 위한 오픈 소스 기반의 비관계형 데이터베이스 관리 시스템입니다. 

 

데이터베이스를 이용하면 데이터를 영속적으로 관리할 수 있지만, 입출력에 다소 시간이 걸리기 때문에 실시간 서비스에서는 더 적합한 저장소를 사용할 필요가 있습니다. 이 Redis는 메모리 기반의 저장소이기 때문에 필요한 정보를 빠르게 저장하고 가져올 수 있는 실시간 서비스에 적합한 저장소입니다.

 

Redis는 메모리 기반의 키-값(Key-Value) 저장소로, 쉽게 설명하면 메모리를 캐시처럼 사용하면서 데이터를 빠르게 입출력할 수 있도록 해주는 저장소입니다. 모든 데이터를 메모리에 저장하기때문에 읽기와 쓰기 명령이 매우 빠릅니다. 그런데 메모리는 휘발성이기 때문에 시스템이 꺼지면 모든 데이터가 날아가게 됩니다. 따라서 데이터를 지속적으로 유지하기 위해 모든 작업을 로그에 기록해서 디스크에 저장한 후, 시스템을 구동할 때 로그를 기반으로 데이터를 다시 메모리에 올리는 방식을 사용합니다.

 

따라서 전체 데이터를 영구히 저장하기보다는, 캐시처럼 휘발성이나 임시성 데이터를 저장하는 데 많이 사용됩니다. Casssandra나 HBase와 같이 NoSQL DBMS로 분류되기도 하고, memcached와 같은 In memory 솔루션으로 분류되기도 합니다. 

 

메모리를 이용하여 고속으로 <key, value> 스타일의 데이터를 저장하고 불러올 수 있는 시스템 정도로만 이해하면 되겠습니다.

 

Redis 특징

1. Key/Value

2. 다양한 데이터 타입

  • String - 일반적인 문자열로 최대 512Mbyte 길이까지 지원, Text 문자열뿐만 아니라 Integer와 같은 숫자나 JPEG와 같은 Binary 파일까지 저장 가능
  • Set - String의 집합으로 여러 개의 값을 하나의 Value 내에 넣을 수 있음. 정렬되지 않은 집합형으로, 한 Key에 중복된 데이터는 존재하지 않음. Set에 포함된 요소의 수와 관계없이 일정한 시간으로 체크를 할 수 있는 것이 장점.
  • Sorted Sets - Set에 Score라는 필드가 추가된 데이터형으로, Score는 일종의 가중치. 데이터는 오름차순으로 내부 정렬되며, 정렬하는 기준이 Score. Score 값은 중복될 수 없음.
  • Lists - String의 집합으로 Set과 유사하지만 일종의 양방향 Linked List. List 앞과 뒤에서 push/pop 연산을 사용해 데이터를 추가 및 삭제가 가능.
  • Hashes - value에 field와 string value 쌍으로 이루어진 테이블을 저장. 객체를 나타내는 데 사용 가능. 형태는 List와 비슷하나 필드명, 필드값의 연속으로 이루어짐.

 

Redis 저장방식

레디스는 In-Memory 데이터베이스입니다. 즉, 모든 데이터를 메모리에 저장하고 조회합니다. 기존 관계형 데이터베이스(Oracle, MySQL) 보다 훨씬 빠른데 그 이유는 메모리 접근이 디스크 접근보다 빠르기 때문입니다. 하지만 빠르다는 것은 레디스의 여러 특징 중 일부분입니다. 다른 In-Memory 데이터베이스(ex. Memcached) 와의 가장 큰 차이점은 다양한 자료구조 를 지원한다는 것입니다. 레디스는 아래처럼 다양한 자료구조를 Key-Value 형태로 저장합니다.

이러한 다양한 자료구조를 제공하면 왜 좋을까?

바로 개발의 편의성과 난이도 때문입니다.

예를 들어 실시간 랭킹 서버를 구현할 때 관계형 DBMS를 이용한다면 DB에 데이터를 저장하고, 저장된 SCORE 값으로 정렬하여 다시 읽어오는 과정이 필요할 것입니다. 개수가 많아지면 속도가 느려질 텐데요, 이 과정에서 디스크를 사용하기 때문입니다. In-memory 기반으로 서버에서 데이터를 처리하도록 직접 코드를 짤 수도 있겠지만.. 레디스의 Sorted-Set을 이용하는게 더 빠르고 간단한 방법일 것입니다.

레디스는 트랜잭션의 문제도 해결해 줄 수 있습니다. 싱글 스레드로 동작하는 서버의 모든 자료구조는 atomic 하기 때문에, race condition을 피해 데이터의 정합성을 보장하기 쉽습니다.

 

즉, 외부의 Collections을 잘 이용하는 것만으로 개발 시간 단축이 가능하고, 생각하지 못한 여러가지 문제를 줄여줄 수 있으므로 개발자는 비즈니스 로직에 집중할 수 있다는 큰 장점이 존재합니다.

 

Redis의 자료구조

1. String

레디스의 string은 키와 연결할 수 있는 가장 간단한 유형의 값입니다. 레디스의 키가 문자열이므로 이 구조는 문자열을 다른 문자열에 매핑하는 것이라고 볼 수 있습니다.

> set hello world
OK
> get hello
"world"

 

2. List

 일반적인 linked list 의 특징을 갖고 있습니다. 따라서 list 내에 수백만 개의 아이템이 있더라도 head와 tail에 값을 추가할 때 동일한 시간이 소요됩니다. 특정 값이나 인덱스로 데이터를 찾거나 삭제할 수 있습니다.

LPUSH mylist B   # now the list is "B"
LPUSH mylist A   # now the list is “A","B"
RPUSH mylist A   # now the list is “A”,”B","A"

 

3. Hash

 

hash는 field-value 쌍을 사용한 일반적인 해시입니다. key에 대한 filed의 갯수에는 제한이 없으므로 여러 방법으로 사용이 가능합니다.

field와 value로 구성된다는 면에서 hash는 RDB의 table과 비슷합니다. hash key는 table의 PK, field는 column, value는 value로 볼 수 있습니다. key가 PK와 같은 역할을 하기 때문에 key 하나는 table의 한 row와 같습니다. 아래는 일반적으로 사용하는 RDB의 테이블을 레디스의 해시 구조로 나타낸 그림입니다.

 

4. set

set은 정렬되지 않은 문자열의 모음입니다. 일반적인 set이 그렇듯이, 아이템은 중복될 수 없습니다. 교집합, 합집합, 차집합 연산을 레디스에서 수행할 수 있기 때문에 set은 객체 간의 관계를 표현할 때 좋습니다.

 

5. sorted set

sorted set은 set과 마찬가지로 key 하나에 중복되지 않는 여러 멤버를 저장하지만, 각각의 멤버는 스코어에 연결됩니다. 모든 데이터는 이 값으로 정렬되며, 스코어가 같다면 멤버값의 사전순서로 정렬됩니다. sorted set은 주로 sort가 필요한 곳에 사용됩니다.

 

6. 그 외의 것들

  • bit / bitmap: SETBIT, GETBIT 등의 커맨드로 일반적인 비트 연산이 가능합니다. 비트맵을 사용하면 공간을 크게 절약할 수 있다는 장점이 있는데요, 이 내용은 다음번 활용사례에서 자세하게 말씀드리겠습니다.
  • hyperloglogs: 집합의 카디널리티(원소의 갯수)를 추정하기 위한 데이터 구조입니다. (ex. 검색 엔진의 하루 검색어 수) 일반적으로 이를 계산할 때에는 데이터의 크기에 비례하는 메모리가 필요하지만, 레디스의 hyperloglogs를 사용하면 같은 데이터를 여러번 계산하지 않도록 과거의 항목을 기억하기 때문에 메모리를 효과적으로 줄일 수 있습니다. 메모리는 매우 적게 사용하고 오차는 적습니다.
  • Geospatial indexes: 지구상 두 지점의 경도(longitude)와 위도(latitude)를 입력하고, 그 사이의 거리를 구하는 데에 사용됩니다. 내부적으로는 Sorted Set Data Structure를 사용합니다.
  • Stream: 레디스 5.0에서 새로 도입된 로그를 처리하기 위해 최적화된 데이터 타입입니다. 차별화된 다양한 장점이 있지만, 가장 큰 특징은 소비자(Consumer)그룹을 지정할 수 있다는 것입니다. Stream에 대해서는 다음 기회에 더 자세하게 말씀드리겠습니다.

참조 : 

https://edu.goorm.io/learn/lecture/557/%ED%95%9C-%EB%88%88%EC%97%90-%EB%81%9D%EB%82%B4%EB%8A%94-node-js/lesson/174388/redis%EB%9E%80

https://meetup.toast.com/posts/224

https://meetup.toast.com/posts/225

 

 

 

'개발합시다. > BackEnd 공부' 카테고리의 다른 글

Redis 추가공부 사항  (0) 2021.12.08
Spring Boot 실습 기록 1  (0) 2021.12.07
mongoDB의 모든 것  (0) 2021.12.07
Nginx의 모든 것  (0) 2021.12.07
Django 정리  (0) 2021.12.06

mongoDB란

mongoDB는 문서지향(Document-Oriented) 저장소를 제공하는 NoSQL 데이터베이스 시스템으로, 현존하는 NoSQL 데이터베이스 중 인지도 1위를 유지하고 있습니다.

 

스키마 제약 없이 자유롭고, BSON(Binary JSON) 형태로 각 문서가 저장되며 배열(Array)이나 날짜(Date) 등 기존 RDMS에서 지원하지 않던 형태로도 저장할 수 있기 때문에 JOIN이 필요 없이 한 문서에 좀 더 이해하기 쉬운 형태 그대로 정보를 저장할 수 있다는 것이 특징입니다.

 

특히 mongoDB는 문서지향 데이터베이스로, 이것은 객체지향 프로그래밍과 잘 맞고 JSON을 사용할 때 아주 유용합니다. 따라서 자바스크립트를 기반으로 하는 Node.js와 호환이 매우 좋기 때문에, Node.js에서 가장 많이 사용되는 데이터베이스입니다. 물론 mysql 같은 관계형 데이터베이스 사용도 가능합니다.

 

문서지향 데이터베이스에서는 행 개념 대신 보다 유연한 모델인 문서를 이용하는데, 내장문서와 배열 따위의 표현이 가능해서 복잡한 객체의 계층 관계를 하나의 레코드로 표현할 수 있습니다. 이러한 문서지향 데이터베이스로는 mongoDB 이외에도 10gen, Couchbse, CouchDB 등이 있습니다.

 

 

mongoDB 특징

  • Join이 없으므로 Join이 필요 없도록 데이터 구조화가 필요
  • 다양한 종류의 쿼리문을 지원(필터링, 수집, 정렬, 정규표현식 등)
  • 관리의 편의성
  • 스키마 없는(Schemaless) 데이터베이스를 이용한 신속 개발. 필드를 추가하거나 제거하는 것이 매우 쉬워짐
  • 쉬운 수평 확장성
  • 인덱싱 제공

 

mongoDB 구조

많은 DB가 있고 그 안에 컬렉션이 있고 컬렉션에는 여러개의 Document가 있음

 

Document는 동적 스키마를 갖고있기 때문에, 같은 Collection 안의 Document끼리 다른 스키마를 갖고 있을 수 있다. 즉, 서로 다른 데이터들을 가지고 있을 수 있다.

 

mongoDB 장단점

장점

쌓아놓고 삭제가 없는 경우가 가장 적합

  • Flexibility : Schema-less라서 어떤 형태의 데이터라도 저장할 수 있다.
  • Performance : Read & Write 성능이 뛰어나다. 캐싱이나 많은 트래픽을 감당할 때 써도 좋다.
  • Scalability : 애초부터 스케일아웃 구조를 채택해서 쉽게 운용가능하다. Auto sharding 지원
  • Deep Query ability : 문서지향적 Query Language 를 사용하여 SQL 만큼 강력한 Query 성능을 제공한다.
  • Conversion / Mapping : JSON형태로 저장이 가능해서 직관적이고 개발이 편리하다.
단점

정합성이 떨어지므로 트랜잭션이 필요한 경우에는 부적합

  • JOIN이 없다. join이 필요없도록 데이터 구조화 필요
  • memory mapped file으로 파일 엔진 DB이다. 메모리 관리를 OS에게 위임한다. 메모리에 의존적, 메모리 크기가 성능을 좌우한다.
  • SQL을 완전히 이전할 수는 없다.
  • B트리 인덱스를 사용하여 인덱스를 생성하는데, B트리는 크기가 커질수록 새로운 데이터를 입력하거나 삭제할 때 성능이 저하된다. 이런 B트리의 특성 때문에 데이터를 넣어두면 변하지않고 정보를 조회하는 데에 적합하다

 

데이터 저장구조

MongoDB는 기본적으로 memory mapped file(OS에서 제공되는 mmap을 사용)을 사용한다. 데이터를 쓰기할때, 디스크에 바로 쓰기작업을 하는 것이 아니라 논리적으로 memory 공간에 쓰기를 하고, 일정 주기에 따라서, 이 메모리 block들을 주기적으로 디스크에 쓰기한다. 이 디스크 쓰기 작업은 OS에 의해서 이루어 진다.

 

OS에 의해서 제공되는 가상 메모리를 사용하게 되는데, 물리 메모리 양이 작더라도 가상 메모리는 훨씬 큰 공간을 가질 수 있다. 가상 메모리는 페이지(Page)라는 블럭 단위로 나뉘어 지고, 이 블럭들은 디스크 블럭에 매핑되고, 이 블럭들의 집합이 하나의 데이터 파일이 된다.

 

메모리에 저장되는 내용은 실제 데이터 블록과, 인덱스 자체가 저장된다. MongoDB에서 인덱스를 남용하지 말라는 이야기가 있는데, 이는 인덱스를 생성 및 업데이트 하는데 자원이 들어갈 뿐더러, 인덱스가 메모리에 상주하고 있어야 제대로 된 성능을 낼 수 있기 때문이기도 하다.

 

만약에 물리 메모리에 해당 데이터 블록이 없다면, 페이지 폴트가 발생하게 되고, 디스크에서 그 데이터 블록을 로드하게 된다. 물론 그 데이터 블록을 로드하기 위해서는 다른 데이터 블록을 디스크에 써야한다.

 

즉, 페이지 폴트가 발생하면, 페이지를 메모리와 디스카 사이에 스위칭하는 현상이 일어나기 때문에 디스크IO가 발생하고 성능 저하를 유발하게 된다.

 

즉 메모리 용량을 최대한 크게 해서 이 페이지폴트를 예방하라는 이야기이다. 그러나, 페이지 폴트가 아예 발생 안할 수는 없다.(1TB의 데이터를 위해 메모리를 진짜 1TB만큼 올릴 수는 없다.) 그래서 페이지 폴트를 줄이는 전략으로 접근 하는 것이 옳은 방법이다.

 

페이지 폴트시 디스크로 write되는 데이터는 LRU 로직에 의해서 결정된다. 그래서, 자주 안쓰는 데이터가 disk로 out되는데, 일반적인 애플리케이션에서 자주 쓰는 데이터의 비율은 그리 크지 않다. 이렇게 자주 액세스되는 데이터를 Hot Data라고 하는데, 이 데이터들이 집중되서 메모리에 올라가도록 Key 설계를 하는 것이 핵심이다. 전체 데이터를 scan하는 등의 작업을 하게 되면, 무조건 페이지 폴트가 발생하기에 table scan이 필요한 시나리오는 별도의 index table(summary table)을 만들어서 사용하는 등의 전략이 필요하다.

 

구성요소

mongoDB는 데이터를 관리하기 위한 형식으로 데이터 입출력 시에는 JSON 형식을 사용하고 데이터 저장 시에는 BSON 형식을 사용합니다. 다음은 JSON과 BSON 형식에 대한 자세한 내용입니다.

 

SQL과 비교

 


참조 : 

명령어 : https://blog.naver.com/PostView.naver?blogId=itinstructor&logNo=221465476441&parentCategoryNo=&categoryNo=43&viewDate=&isShowPopularPosts=true&from=search 

https://meetup.toast.com/posts/276

https://meetup.toast.com/posts/275

https://edu.goorm.io/learn/lecture/557/%ED%95%9C-%EB%88%88%EC%97%90-%EB%81%9D%EB%82%B4%EB%8A%94-node-js/lesson/174384/mongodb%EB%9E%80

https://hwanine.github.io/database/MongoDB/

https://coding-start.tistory.com/273

https://rastalion.me/mongodb%EB%9E%80/

'개발합시다. > BackEnd 공부' 카테고리의 다른 글

Spring Boot 실습 기록 1  (0) 2021.12.07
Redis의 모든 것  (0) 2021.12.07
Nginx의 모든 것  (0) 2021.12.07
Django 정리  (0) 2021.12.06
Django 기초 정리  (0) 2021.12.06

 

Nginx란?

Nginx는 경량 웹 서버입니다.
클라이언트로부터 요청을 받았을 때 요청에 맞는 정적 파일을 응답해주는 HTTP Web Server로 활용되기도 하고,
Reverse Proxy Server로 활용하여 WAS 서버의 부하를 줄일 수 있는 로드 밸런서로 활용되기도 합니다.

 

웹서버(web server) : 주로 정적 콘텐츠(이미지나 정적 HTML 등)를 제공하기 위해 설계되었으며 동적 콘텐츠 요청을 식별하여 앱서버(app server)로 요청을 전달하는 역할을 수행하는 서버.
역방향 프록시(reverse proxy) 
: 클라이언트의 요청을 받아 웹서버로 전달하여 응답을 받은 후 다시 클라이언트에 전달하는 역할을 수행하는 서버. 즉, 클라이언트와 웹서버 사이에 존재하는 서버

Nginx 흐름

Nginx는 Event-Driven 구조로 동작하기 때문에 한 개 또는 고정된 프로세스만 생성하여 사용하고,
비동기 방식으로 요청들을 Concurrency 하게 처리할 수 있습니다.
위의 그림에서 보이듯이 Nginx는 새로운 요청이 들어오더라도 새로운 프로세스와 쓰레드를 생성하지 않기 때문에 프로세스와 쓰레드 생성 비용이 존재하지 않고, 적은 자원으로도 효율적인 운용이 가능합니다.
이러한 Nginx의 장점 덕분에 단일 서버에서도 동시에 많은 연결을 처리할 수 있습니다.

 

Nginx 구조

Nginx는 하나의 Master Process와 다수의 Worker Process로 구성되어 실행됩니다.

Master Process는 설정 파일을 읽고, 유효성을 검사 및 Worker Process를 관리합니다.
모든 요청은 Worker Process에서 처리합니다. Nginx는 이벤트 기반 모델을 사용하고, Worker Process 사이에 요청을 효율적으로 분배하기 위해 OS에 의존적인 메커니즘을 사용합니다.
Worker Process의 개수는 설정 파일에서 정의되며, 정의된 프로세스 개수와 사용 가능한 CPU 코어 숫자에 맞게 자동으로 조정됩니다.

 

Nginx 장점

  1. 로드 밸런싱 : 요청이 많은 사이트를 운영하는 경우 하나의 서버가 아닌 여러 대의 서버를 두고 운영을 하게 됩니다. 그럴 경우 특정 서버에만 요청이 몰리지 않도록 하는 역할을 NGINX가 수행하게 됩니다.
  2. 공격으로 부터 보호 : NGINX를 사용하면 웹사이트나 서비스에서 실제 서버의 IP 주소를 필요로 하지 않기 때문에 DDoS와 같은 공격이 들어와도 NGINX를 공격하게 되지 실제 서버에는 공격이 들어오는 것을 막을 수 있습니다.
  3. 캐싱 : NGINX는 콘텐츠를 캐싱할 수 있어 결과를 더 빠르게 응답하여 성능을 높일 수 있습니다.

 

Apache와 비교

Apache

- 쓰레드 / 프로세스 기반 구조로 요청 하나당 쓰레드 하나가 처리하는 구조

- 사용자가 많으면 많은 쓰레드 생성, 메모리 및 CPU 낭비가 심함

- 하나의 쓰레드 : 하나의 클라이언트 라는 구조

 

Nginx 방식

 


참고 : 

https://velog.io/@wijihoon123/Nginx%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80

https://medium.com/@su_bak/nginx-nginx%EB%9E%80-cf6cf8c33245

https://azderica.github.io/00-network-nginx/

https://whatisthenext.tistory.com/123

 

 

'개발합시다. > BackEnd 공부' 카테고리의 다른 글

Redis의 모든 것  (0) 2021.12.07
mongoDB의 모든 것  (0) 2021.12.07
Django 정리  (0) 2021.12.06
Django 기초 정리  (0) 2021.12.06
네트워크 관련 면접 질문 정리 ①  (0) 2021.10.06

특징

1. MVT 패턴을 사용한다.

MVC모델이랑 사실상 동일하다고 생각하면 됨

View -> Templage / Controller -> View로 표현한다.

Model :  DB에 저장되는 데이터
Template는 유저에게 보여지는 UI
View는 데이터를 처리하고 Template에 데이터를 전달

 

2. 강력한 ORM 기능 지원

ORM(Obect Relational Mapping, 객체관게 매핑)을 통해 객체와 RDBMS를 연결해준다.

따로 SQL문을 만들지 않고, 생성한 클래스(객체)를 바로 DB와 연동할 수 있습니다.

 

3. 소스코드의 변경 사항을 자동으로반영합니다.

장고는 자동으로 ..py파일의 변동을 감지하고 자동으로 반영해줍니다. 그래서 코드를 변경할때마다 수동으로 웹 서버를 재실행할 피료가 없습니다.

 

Flask와 비교

Flask는 Micro Framework를 철학으로 만들어진 프레임워크라서 "웹개발을 위한 최소한의 기능만 지원"합니다.

반대로 장고는 웹개발을 위한 거의 모든 기능을 지원합니다. 다만, 프레임워크가 제시하는 틀에서 벗어날 수 없는 단점이 있죠

 

쉽게 설명하면 Flask는 레고 / Django는 프라모델이라 생각하면 됩니다.

 

장단점

장점

1. 파이썬을 기반으로 한 만큼 진입장벽이 낮은 편이다.

2. 생산성이 높고 개발 비용을 꽤 절감시킬 수 있다.

3. URL디자인이 매우 편리하다.(정규표현식 사용 가능, 함수와 URL을 1:1 매칭 가능)

4. 파이썬 기반의 머신러닝, 데이터 분석 라이브러리와 융합하여 사용하기 편하다.

5. 출시된지 오랜 시간이 흘렀고, Instagram처럼 성공적으로 도입된 사례가 많은 만큼 안정성과 기능이 충분히 검증되었다.

6. 커뮤니티 활동이 활발하고, 관련 문서(자료)가 많다.

단점

1. 파이썬을 기반으로 한 만큼 파이썬의 단점이 전부 포함된다.

2. 다른 웹 프레임워크에 비해 비교적 속도가 느리다.

3. 숙련되기 위해선 장고 자체에 대한 이해도 뿐만 아니라 파이썬에 대해서도 높은 이해도를 요구한다.

4. 관련 문서도 많고 커뮤니티 활동도 활발한데.. 대부분 한글화가 안되어있다..

 

 

장고를 사용하는 사이트

  • 인스타그램
  • NASA
  • 비버켓
  • Disqus
  • 모질라

 

구동방식

초록색이 Django

URL Dispactcher을 통해 URL을 분석하고 적합한 View로 전달한 후 View는 Model을 통해 데이터를 받아오고 View에게 주면서 View는 Template를 통해 유저에게 response를 넘긴다.

 

좀 더 자세한 사진

  • WSGI(Web server gateway interface) : 장고와 웹서버를 연결해주는 역할. 웹서버(Nginx)로부터 서버사이드 요청을 받으면 WSGI를 통해 장고로 전달하는 역할을 수행한다.
  • URL Resoluion: 정규표현식으로 구성. 그에 맞는 특정한 View로 전달
  • VIEW : URL을 통해 받은 사용자 request에 따른 데이터의 처리를 결정하고, MODEL에 신호를 보냄. 또한, MODEL에서 받은 정보를 가공하기도 함. 웹페이지나 콘텐츠들은 VIEW를 통해 전달되며, 파이썬의 function(method)으로 나타난다.
  • MODEL : DB에서 데이터를 가지고 오고, 그 데이터를 각각의 클래스에 넣는다.(변수 지정). SETTING이외 부가적인 메타데이터를 가진 데이터베이스의 구조(layout).
  • TEMPLATE : UI작업이 이루어지는곳으로, VIEW에서 받은 정보를 HTML등으로 가공하는 로직이 들어가 있다. 3세대 부터는 프론트엔드 개발자가 담당.
  • FORM : model, template에 있는 UI관리 (?)

추가내용

장고의 manage.py runserver 명령어는 단일 쓰레드로 작동하여 테스트 용도로써는 적합하나, request요청이 많아질 경우 처리 능력이 떨어지므로, production 환경에는 적합하지 않다.

따라서, 배포시에는 이를 대체할 Gunicorn 과 같은 프로그램을 사용하는게 적합하다.

 

 

Runserver의 비밀

  • runserver뒤에는 IP주소와 포트번호가 숨겨져 있다-포트(Port): IP로 찾은 컴퓨터 내부에서 실행되고 있는 프로그램이나 서비스를 구분하는 값 → IP로 컴퓨터를 찾고 그 안에서 포트를 가지고 프로그램을 찾는 것임
  • 아이피 주소(IP Address): Internet Protocol의 앞 글자를 딴 것으로 네트워크 상에서 다른 컴퓨터와 내 컴퓨터를 구별하는 일종의 주소
  • 개발서버(runserver)는 확인용이므로 배포시에는 웹서버를 따로 사용해야 한다

 

파일구조

 

참고 : 

https://tibetsandfox.tistory.com/6

https://velog.io/@rosewwross/Django%EB%9E%80-sck638v59x

https://velog.io/@ally0526/2.-django-%EA%B5%AC%EC%A1%B0-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0

'개발합시다. > BackEnd 공부' 카테고리의 다른 글

mongoDB의 모든 것  (0) 2021.12.07
Nginx의 모든 것  (0) 2021.12.07
Django 기초 정리  (0) 2021.12.06
네트워크 관련 면접 질문 정리 ①  (0) 2021.10.06
데이터베이스 관련 면접 질문 정리 ②  (0) 2021.09.30

정의

Django는 파이썬으로 만들어진 무료 오픈소스 웹 애플리케이션 프레임워크입니다.

 

어떤일을 할까?

웹서버에 요청이 오면 장고로 전달됩니다.

장고 urlresolver는 웹페이지 주소를 보고 무엇을 할지 확인합니다.

view에 어떤 할일을 할지 찾아서 실행시킵니다. 

view는 할일을 다하고 답장을 생성하여 장고는 그 답장을 그 사용자의 웹 브라우저에 보내주는 역할을 합니다.

 

모델

장고에 모델을 저장하면 그 내용이 데이터베이스(DB)에 저장됩니다.

예를 들어 블로그 글 모델을 생각해볼게요

//blog/models.py
from django.conf import settings
from django.db import models
from django.utils import timezone


class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    text = models.TextField()
    created_date = models.DateTimeField(
            default=timezone.now)
    published_date = models.DateTimeField(
            blank=True, null=True)

    def publish(self):
        self.published_date = timezone.now()
        self.save()

    def __str__(self):
        return self.title

이렇게 Post 모델을 만들어 줄 수 있습니다.

 

그 이후로는

python manage.py makemigrateions blog

python manage.py migrate blog

로 글 모델이 데이터베이스에 migration을 해줍니다.

 

URL 설정

전체 폴더의 urls.py에 들어가서

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls')),
]

이렇게 코드를 바꿔주면, ''(아무것도 없음)이 url에 들어오면, blog.urls를 찾는다는 뜻입니다

from django.urls import path
from . import views

urlpatterns = [
    path('', views.post_list, name='post_list'),
]

그리고 blog/urls.py에 이렇게 추가해주면, ''가 들어오면 views의 post_list를 사용한다는 뜻입니다.

 

참고 : 

https://tutorial.djangogirls.org/ko/

 

들어가며 · HonKit

1차 : 2015. 10. 1. 이수진, 함기훈, 박제권, 조혜선, 정광윤, 임정훈, 문지영, 김휘경, 송석리, 여형석, 심혜민

tutorial.djangogirls.org

 

문제 설명

풀이

이번에는 다른 방식으로 BFS를 풀었다.

시간을 계속 들고다니고 간단한 BFS처럼 풀어주면 끝이다.

 

코드

import java.util.*;
import java.io.*;

class Main {
    static int N, K;
    static int minTime = Integer.MAX_VALUE;
    static boolean[] visit = new boolean[100001];

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        K = Integer.parseInt(st.nextToken());

        if (N >= K) {
            System.out.println((N-K));
            return;
        }
        else{
            bfs();
            System.out.println(minTime);
        }
    }

    public static void bfs() {
        Queue<int[]> q = new LinkedList<>();
        q.offer(new int[] {N, 0});

        while(!q.isEmpty()) {
            int[] node = q.poll();
            visit[node[0]] = true;
            if(node[0] == K) minTime = Math.min(minTime, node[1]);

            if(node[0] * 2 <= 100000 && visit[node[0] * 2] == false) q.offer(new int[] {node[0] * 2, node[1]});
            if(node[0] + 1 <= 100000 && visit[node[0] + 1] == false) q.offer(new int[] {node[0] + 1, node[1] + 1});
            if(node[0] - 1 >= 0 && visit[node[0] - 1] == false) q.offer(new int[] {node[0] - 1, node[1] + 1});
        }
    }

}

https://www.acmicpc.net/problem/13549

 

13549번: 숨바꼭질 3

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일

www.acmicpc.net

 

문제 설명

풀이

당연히 최소거리니까 DP가 아니라면 BFS문제이다.

일반적으로 최소거리를 찾으면 끝내는게 아니라, 최소거리가 나올때 마다 카운트를 추가해야한다

다소 어려운 방법으로 풀었지만, minTime 보다 time[now]가 크면 이미 조건 불만족이니 더 안간다

그리고 이렇게 계속해서 조건부적으로 시간을 체크해주면 가장 빠르게 할 수있다

코드

import java.util.*;
import java.io.*;

class Main {
    static int N, K;
    static int[] time = new int[100001];
    static int minTime = 987654321;
    static int count = 0;

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;

        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        K = Integer.parseInt(st.nextToken());

        if (N >= K) {
            System.out.println((N-K) + "\n1");
            return;
        }

        bfs();

        System.out.println(minTime + "\n" + count);
    }

    static void bfs() {
        Queue<Integer> q = new LinkedList<Integer>();

        q.add(N);
        time[N] = 1;

        while (!q.isEmpty()) {
            int now = q.poll();

            // now 방문 시간이 최소 시간보다 크면 뒤는 더 볼 필요 없음
            if (minTime < time[now]) return;

            for (int i=0; i<3; i++) {
                int next;

                if (i == 0)         next = now + 1;
                else if (i == 1)    next = now - 1;
                else                next = now * 2;

                if (next < 0 || next > 100000) continue;

                if (next == K) {
                    minTime = time[now];
                    count++;
                }

                // 첫 방문이거나 (time[next] == 0)
                // 이미 방문한 곳이어도 같은 시간에 방문했다면 (time[next] == time[now] + 1)
                // 경우의 수에 추가될 수 있기 때문에 Queue 에 한번 더 넣어줌
                if (time[next] == 0 || time[next] == time[now] + 1) {
                    q.add(next);
                    time[next] = time[now] + 1;
                }
            }
        }
    }
}

https://www.acmicpc.net/problem/12851

 

12851번: 숨바꼭질 2

수빈이는 동생과 숨바꼭질을 하고 있다. 수빈이는 현재 점 N(0 ≤ N ≤ 100,000)에 있고, 동생은 점 K(0 ≤ K ≤ 100,000)에 있다. 수빈이는 걷거나 순간이동을 할 수 있다. 만약, 수빈이의 위치가 X일 때

www.acmicpc.net

 

문제 설명

풀이

바이러스를 퍼트리는건 똑같이  BFS로 퍼트리면 된다.

바이러스중에서 어떤 것을 활성화 시키면 가장 빨리 될지를 찾아야 되니, Backtracking으로 모든 경우의 수를 돌아볼 수 있게 해주면 끝!

 

중요코드 (백트래킹)

백트래킹이 자꾸 헷갈리는데 start와 count(종료조건)을 꼭 해두고 불필요한 반복은 안하게 만들자

    // 백트래킹으로 M 개의 바이러스를 선택하는 Combination 구현
    static void selectVirus(int start, int selectCount) {
        if (selectCount == M) {
            spreadVirus(originEmptySpace);
            return;
        }

        for (int i = start; i < viruses.size(); i++) {
            active[selectCount] = viruses.get(i);
            selectVirus(i + 1, selectCount + 1);
        }
    }

 

코드

import java.util.*;
import java.io.*;

class Main {
    static class Virus {
        int x, y, time;

        Virus(int x, int y, int time) {
            this.x = x;
            this.y = y;
            this.time = time;
        }
    }

    static int N, M;
    static int[][] arr;
    static int[] dx = {-1, 1, 0, 0};
    static int[] dy = {0, 0, -1, 1};
    static List<Virus> viruses = new ArrayList<>();
    static Virus[] active;
    static int resultMinTime = Integer.MAX_VALUE;
    static int originEmptySpace = 0;

    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st;

        // input
        st = new StringTokenizer(br.readLine());
        N = Integer.parseInt(st.nextToken());
        M = Integer.parseInt(st.nextToken());
        
        arr = new int[N][N];
        active = new Virus[M];

        for (int i = 0; i < N; i++) {
            st = new StringTokenizer(br.readLine());

            for (int j = 0; j < N; j++) {
                arr[i][j] = Integer.parseInt(st.nextToken());

                if (arr[i][j] == 0) {
                    originEmptySpace++;
                } else if (arr[i][j] == 2) {
                    viruses.add(new Virus(i, j, 0));
                }
            }
        }

        // solution
        if (originEmptySpace == 0) {
            System.out.println(0);
        } else {
            selectVirus(0, 0);
            System.out.println(resultMinTime == Integer.MAX_VALUE ? -1 : resultMinTime);
        }
    }

    // 백트래킹으로 M 개의 바이러스를 선택하는 Combination 구현
    static void selectVirus(int start, int selectCount) {
        if (selectCount == M) {
            spreadVirus(originEmptySpace);
            return;
        }

        for (int i = start; i < viruses.size(); i++) {
            active[selectCount] = viruses.get(i);
            selectVirus(i + 1, selectCount + 1);
        }
    }

    // BFS 로 바이러스를 퍼트린다
    static void spreadVirus(int emptySpace) {
        Queue<Virus> q = new LinkedList<>();
        boolean[][] infected = new boolean[N][N];

        for (int i = 0; i < M; i++) {
            Virus virus = active[i];
            infected[virus.x][virus.y] = true;
            q.add(virus);
        }

        while (!q.isEmpty()) {
            Virus virus = q.poll();

            for (int i = 0; i < 4; i++) {
                int nx = virus.x + dx[i];
                int ny = virus.y + dy[i];

                if (nx < 0 || nx >= N || ny < 0 || ny >= N) continue;
                if (infected[nx][ny] || arr[nx][ny] == 1) continue;

                if (arr[nx][ny] == 0) {
                    emptySpace--;
                }

                if (emptySpace == 0) {
                    resultMinTime = Math.min(resultMinTime, virus.time + 1);
                    return;
                }

                infected[nx][ny] = true;
                q.add(new Virus(nx, ny, virus.time + 1));
            }
        }
    }
}

 

문제 설명

풀이

역시나 결국 같은 유형의 병사들의 숫자를 구해서 제곱을 해서 각각 따로 더해주면 된다 간단한 BFS문제

 

코드

import java.util.*;
import java.io.*;

class Main {
    static int N,M;
    static int c =0;
    static Character[][] map;
    static int[][] visit;
    static int[] dx = {1, -1, 0, 0};
    static int[] dy = {0, 0, 1, -1};
    static Queue<int[]> que = new LinkedList<>();

    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        M = sc.nextInt();
        N = sc.nextInt();

        map = new Character[N][M];
        visit = new int[N][M];
        int me = 0;
        int you = 0;
        sc.nextLine();
        for (int i = 0; i < N; i++) {
            String str = sc.nextLine();
            for (int j = 0; j < M; j++) {
                map[i][j] = str.charAt(j);
            }
        }

        for(int i=0; i<N; i++){
            for(int j=0; j<M; j++){
                if (visit[i][j] != 1){
                    c=1;
                    que.add(new int[] {i,j});
                    visit[i][j] = 1;
                    bfs();
                    if(map[i][j]=='W')
                        me += c*c;
                    else
                        you += c*c;
                }
            }
        }

        System.out.println(me + " " + you);
    }

    static void bfs() {
        // bfs 시작
        while(!que.isEmpty()) {
            int[] out = que.poll();
            int x = out[0];
            int y = out[1];

            for(int i=0; i<4; i++) {
                int nx = x + dx[i];
                int ny = y + dy[i];

                if(0 > nx || nx >= N || 0 > ny || ny >= M || visit[nx][ny] == 1) {
                    continue;
                }
                if (map[nx][ny] != map[x][y])
                    continue;
                c++;
                visit[nx][ny] = 1;
                que.add(new int[] {nx, ny});
            }
        }
    }
}

https://www.acmicpc.net/problem/1303

 

1303번: 전쟁 - 전투

첫째 줄에는 전쟁터의 가로 크기 N, 세로 크기 M(1 ≤ N, M ≤ 100)이 주어진다. 그 다음 두 번째 줄에서 M+1번째 줄에는 각각 (X, Y)에 있는 병사들의 옷색이 띄어쓰기 없이 주어진다. 모든 자리에는

www.acmicpc.net

 

+ Recent posts