4 분 소요


MVC 패턴

애플리케이션을 Model-View-Controller 세 가지 역할로 구분한 개발 방법론이다.
Controller가 Http 요청을 받으면 파라미터 검증을 하고, 비즈니스 로직을 실행하게 되며, Model을 통해 데이터를 가져오고 그 데이터를 바탕으로 View를 통해 화면에 출력해준다.

탄생 이유

예전에는 서블릿으로 개발할 때는 Html 코드가 자바 코드에 섞여있어 복잡했지만, JSP를 사용한 덕분에 HTML 코드에서 동적으로 변경이 필요한 부분만 자바 코드를 사용했다.
하지만, JSP는 데이터를 조회하고, 화면을 렌더링 하는 등 너무 많은 역할을 하게 되어서 MVC 패턴이 탄생했다.
하나의 서블릿이나 JSP만으로 비즈니스 로직과 뷰 렌더링까지 처리하게 되면 너무 많은 역할을 하게되고, 결과적으로 유지 보수가 어려워진다.
UI를 일부 수정하는 일과 비즈니스 로직을 수정하는 일은 각각 다르게 발생할 가능성이 높고, 서로 영향을 주지 않기 때문이다.
또한, JSP 같은 View 템플릿은 화면을 렌더링 하는데 최적화 되어 있기 때문에 이 업무만 담당하는 것이 가장 효율적이다.


DB 옵티마이저

옵티마이저는 SQL을 가장 효율적으로 수행할 최적의 처리 경로를 생성해 주는 DBMS의 핵심 엔진이다.

SQL 최적화 과정

  1. 사용자가 작성한 쿼리 수행을 위해 실행 계획을 찾는다.
  2. Data Dictionary에 미리 수집해 놓은 통계 정보를 이용해 각 실행 계획의 예상 비용을 산정한다.
  3. 각 실행 계획을 비교하여 최저 비용을 갖는 계획을 선택한다.

옵티마이저의 종류는 규칙 기반 옵티마이저비용 기반 옵티마이저가 있다.

규칙 기반 옵티마이저

실행 속도가 빠른 순으로 규칙을 세워두고, 우선 순위가 높은 방법을 채택하는 옵티마이저.

비용 기반 옵티마이저

비용을 기반으로 최적화를 수행.


Index Scan / Full Scan

Full Table Scan

  • 테이블에 존재하는 모든 데이터를 읽어 가면서 조건에 맞으면 추출하고, 맞지 않으면 버리는 방식.
  • Oracle의 경우 테이블의 고수위 마크(HWM, High Water Mark) 아래의 모든 블록을 읽는다.
    • HWM : 테이블에 데이터가 쓰여졌던 블록 상의 최상위 위치(현재는 데이터가 존재하지 않을 수도 있음)
  • 일반적으로 블록들은 서로 인접되어 있기 때문에, Full Table Scan은 한 번의 I/O에 여러 블록을 옮겨온다.
    • 한 번의 I/O에 데이터를 다중 블록 단위로 메모리에 가져오기 때문에, Row당 소요되는 입출력 비용이 Index Scan에 비해 적다.

Index Scan

  • Index값을 기반으로 데이터를 추출하는 엑기스 기법
  • Index를 이용해 몇 번의 I/O만으로 원하는 데이터를 쉽게 찾을 수 있다.

정리

대용량 데이터 중에서 극히 일부의 데이터를 찾을 때, Index Scan 방식은 인덱스를 이용해 몇 번의 I/O만으로 원하는 데이터를 쉽게 찾을 수 있다.
그러나 Full Table Scan은 테이블의 모든 데이터를 읽으면서 원하는 데이터를 찾아야 하기 때문에 비효율적인 검색을 하게 된다.
하지만 반대로, 테이블의 대부분의 데이터를 찾을 때는 한 블록씩 읽는 Index Scan보다 한번에 여러 블록씩 읽는 Full Table Scan 방식이 유리할 수 있다.


코드 리팩토링

프로그램의 외부 동작은 그대로 둔 채로 내부의 코드를 개선하는 것.

리팩토링이 필요한 코드

  • 중복 코드
  • 긴 메소드
  • 거대한 클래스 등


클린 코드

가독성이 높은 코드

  • 네이밍이 잘 되어야 함(메서드 명이나 변수명만으로도 용도 파악 가능)
  • 중복이 없어야 함
  • 주석 달기
  • 클래스 혹은 메서드가 한 가지 일만 처리해야 함


클린코드는 단순히 가독성을 높이기 위한 작업이라면, 리팩토링은 클린 코드를 포함한 유지보수를 위한 코드 개선이 이루어진다.


자바의 컬렉션

컬렉션(Collection)은 많은 데이터를 효율적으로 관리하기 위한 자료구조를 말하며, JCF(Java Collection Framework)는 컬렉션을 구현하는데 필요한 클래스를 정의하는 인터페이스를 제공한다.

Collection은 크게 List, Set, Map 세 가지로 분류할 수 있다.
Map의 경우 Collection 인터페이스를 상속받고 있지 않지만 Collection으로 분류된다.

Set Interface

순서를 유지하지 않는 데이터의 집합으로, 데이터의 중복을 허용하지 않는다.

  • HashSet
    • null 값 허용
    • 순서를 예측할 수 없음
  • TreeSet
    • 정렬 방법을 지정할 수 있음

List Interface

  • LinkedList
    • 연속적인 메모리 위치에 저장되지 않는 선형 데이터 구조
    • 각 노드는 데이터 필드와 다음 노드에 대한 참조를 포함하는 노드로 구성
    • 데이터의 삽입, 삭제가 빈번할 경우, 데이터의 위치 정보만 수정하면 되기에 유용
    • 스택, 큐, 양방향 큐 등을 만들기 위한 용도로 쓰임
  • Vector
    • 내부에서 자동으로 동기화 처리가 일어나 비교적 성능이 좋지 않고, 무거워 잘 쓰이지 않음
  • ArrayList
    • 배열과 동일하게 연속된 메모리 공간을 사용하며, 각 데이터에 대한 인덱스를 가지고 있어 조회 기능에 성능이 뛰어남

Map Interface

Key, Value의 쌍으로 이루어진 데이터의 집합으로, Key의 중복을 허용하지 않으나, Value의 중복은 허용한다.

  • HashTable
    • HashMap보다는 느리지만 동기화 지원
    • null 불가
  • HashMap
    • 중복과 순서가 허용되지 않으며, null값이 올 수 있다.
  • TreeMap
    • 정렬된 순서대로 Key, Value를 저장하여 검색이 빠름

Map과 HashMap의 차이는 특정 키에 대한 값을 찾는 과정에서 HashMap은 HashTable을 이용해 key-value 관계를 유지하며, Map은 red-black tree 알고리즘을 사용한다.

사용 이유

  1. Collection 밑에 있는 모든 클래스는 Collection에서 상속받아 통일된 메서드를 사용한다.
  2. 간단하게 Collection API를 사용하여 구현할 수 있다.


Stream

Java8에서 추가된 Stream은 람다를 활용할 수 있는 기술 중 하나로, 컬렉션의 저장 요소를 하나씩 참조해서 람다식으로 처리할 수 있도록 해주는 내부 반복자이다.
Java8 이전에는 배열 또는 컬렉션 인스턴스를 다루는 방법은 for 또는 foreach문을 돌면서 하나씩 다루는 방식이었다.
Stream은 배열 또는 컬렉션 인스턴스에 함수 여러 개를 조합해 가공된 결과를 얻을 수 있다.

장점

  • 람다를 이용해서 코드의 양을 줄여 간결하게 표현 가능
  • 간단하게 병렬처리가 가능.

Stream 연산

  1. 생성하기 : Stream 인스턴스 생성
  2. 가공하기 : 필터링, 맵핑 등 원하는 결과를 만들어가는 중간 작업
  3. 결과 만들기 : 최종적으로 결과를 만들어내는 작업

특징

  • Collection
    • 모든 값을 메모리에 저장하는 자료구조.
      • 따라서, Collection에 추가하기 전에 미리 계산이 완료되어 있어야 함
    • 외부 반복을 통해 사용자가 직접 반복 작업을 거쳐 요소를 가져올 수 있다.(foreach)
  • Stream
    • 요청할 때만 요소를 계산.
    • 내부 반복을 사용하므로, 추출 요소만 선언해 주면 알아서 반복 처리 진행

외부 반복 / 내부 반복

내부 반복은 작업을 병렬 처리하면서 최적화된 순서로 처리해주기 때문에 성능면에서 내부 반복이 더 좋다.
외부 반복은 명시적으로 컬렉션 항목을 하나씩 가져와서 처리하기 때문에 최적화에 불리하다.


배열 안 중복 제거를 위한 방법

Set

Set은 중복된 값을 허용하지 않는 Collection이다.
따라서, 배열을 Set 타입으로 변환하면 중복을 제거할 수 있다.

String[] arr ={"H","E","L","L","O"};

// 배열 -> HashSet
HashSet<String> hashSet = new HashSet<>(Arrays.toList(arr));

// HashSet -> 배열
String[] resultArr = hashSet.toArray(new String[0]);
  • HashSet은 순서가 보장이 안되기 때문에, 순서를 유지해야 한다면 LinkedHashSet을 사용해야 한다.

Stream / distinct()

Java8 부터는 Stream을 사용해서 배열의 중복을 제거할 수 있다.

String[] arr ={"H","E","L","L","O"};

// 배열 -> Stream -> 중복제거 -> 배열
String[] resultArr = Arrays.stream(arr).distinct().toArray(String[]::new)


람다식

함수를 하나의 식으로 표현한 것으로, 함수를 람다식으로 표현하면 메소드의 이름이 필요 없기 때문에 익명 함수(Anonymous Function)의 한 종류라고 볼 수 있다.

익명 함수

익명 함수란 이름이 없는 함수로, 변수처럼 사용이 가능하며, 매개 변수로 전달이 가능한 특징을 가지고 있다.

등장 이유

불필요한 코드를 줄이고, 가독성을 높이기 위함.

특징

  • 람다식으로 선언된 변수명은 다른 변수명과 중복될 수 없다.
  • 람다식 내에서 사용되는 지역변수는 final이 붙지 않아도 상수로 간주된다.

장점

  1. 코드를 간결하게 만들 수 있다.
  2. 가독성이 높아진다.
  3. 함수를 만드는 과정 없이 한 번에 처리할 수 있어 생산성이 높아진다.
  4. 병렬 프로그래밍이 용이하다.

단점

  1. 재사용이 불가능하다.
  2. 디버깅이 어렵다.
  3. 람다를 남발하면 비슷한 함수가 중복 생성되어 코드가 지저분해질 수 있다.
  4. 재귀로 만들경우에 부적합하다.

댓글남기기