반응형
반응형

 

엘라스틱은 외국에서 만들어졌기 때문에 영어기반의 형태소 분석기가 기본입니다.

한국어에는 다양한 품사와 활용어 등이 있기 때문에 그에 맞는 형태소 분석기가 필요합니다.

 

여러가지가 있지만 엘라스틱에서 정식적으로 채용한 형태소 분석기는 노리형태소 분석기입니다.

 

설치 방법은 엘라스틱 메뉴얼을 참조하시길 바랍니다.

 

https://esbook.kimjmin.net/06-text-analysis/6.7-stemming/6.7.2-nori

 

6.7.2 노리 (nori) 한글 형태소 분석기 - Elastic 가이드북

커뮤니티 한글 형태소 분석기 - 아리랑, 은전한닢, Open Korean Text

esbook.kimjmin.net

 

📝노리형태소 분석기 생성

PUT my_nori
{
  "settings": {
    "analysis": {
      "tokenizer": {
        "nori_none": {
          "type": "nori_tokenizer",
          "decompound_mode": "none"
        },
        "nori_discard": {
          "type": "nori_tokenizer",
          "decompound_mode": "discard"
        },
        "nori_mixed": {
          "type": "nori_tokenizer",
          "decompound_mode": "mixed"
        }
      }
    }
  }
}

 

노리형태소 분석기에는 다양한 분해 단계가 있습니다.

 

  1. none
    • 합성어만 저장합니다
  2. discard
    • 합성어를 분리해 각 어근(의미있는 단어)만 저장합니다
  3. mixed
    • 어근과 합성어 모두 저장합니다. (none + mixed)

 

📝노리형태소 분석기 테스트 (형태소 분석기) [ none ]

# none

GET my_nori/_analyze
{
  "tokenizer": "nori_none",
  "text": [ "백두산이" ]
}

# --- result ---

{
  "tokens" : [
    {
      "token" : "백두산",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "이",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "word",
      "position" : 1
    }
  ]
}​

 

📝노리형태소 분석기 테스트 (형태소 분석기) [ discard ] 

# discard

GET my_nori/_analyze
{
  "tokenizer": "nori_discard",
  "text": [ "백두산이" ]
}

# --- result ---

{
  "tokens" : [
    {
      "token" : "백두",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "산",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "이",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "word",
      "position" : 2
    }
  ]
}​

 

📝노리형태소 분석기 테스트 (형태소 분석기) [ mixed ] 

# mixed

GET my_nori/_analyze
{
  "tokenizer": "nori_mixed",
  "text": [ "백두산이" ]
}


# --- result ---

{
  "tokens" : [
    {
      "token" : "백두산",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "word",
      "position" : 0,
      "positionLength" : 2
    },
    {
      "token" : "백두",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "산",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "이",
      "start_offset" : 3,
      "end_offset" : 4,
      "type" : "word",
      "position" : 2
    }
  ]
}
 
 
 
 
 

 

  • 이러한 기준으로 품사 분해되어 저장됩니다 이걸 이용해 필요 없는 품사를 없앨 수 있습니다
  • 예를 들어 IC 감탄사 "아!" 이런 부분은 검색에 의미가 없죠

 

📝불용어 (stopwords)

PUT my_pos
{
  "settings": {
    "index": {
      "analysis": {
        "filter": {
          "my_pos_f": {
            "type": "nori_part_of_speech",
            "stoptags": [
              "NR"
            ]
          }
        }
      }
    }
  }
}

 

이런식으로 필터를 추가해 분석기 튜닝이 가능합니다.

 

📝상세 형태소 분석 

GET _analyze
{
  "tokenizer": "nori_tokenizer",
  "text": "동해물과 백두산이",
  "explain": true
}

# --- result ---

{
  "detail" : {
    "custom_analyzer" : true,
    "charfilters" : [ ],
    "tokenizer" : {
      "name" : "nori_tokenizer",
      "tokens" : [
        {
          "token" : "동해",
          "start_offset" : 0,
          "end_offset" : 2,
          "type" : "word",
          "position" : 0,
          "bytes" : "[eb 8f 99 ed 95 b4]",
          "leftPOS" : "NNP(Proper Noun)",
          "morphemes" : null,
          "posType" : "MORPHEME",
          "positionLength" : 1,
          "reading" : null,
          "rightPOS" : "NNP(Proper Noun)",
          "termFrequency" : 1
        },
        {
          "token" : "물",
          "start_offset" : 2,
          "end_offset" : 3,
          "type" : "word",
          "position" : 1,
          "bytes" : "[eb ac bc]",
          "leftPOS" : "NNG(General Noun)",
          "morphemes" : null,
          "posType" : "MORPHEME",
          "positionLength" : 1,
          "reading" : null,
          "rightPOS" : "NNG(General Noun)",
          "termFrequency" : 1
        },
        {
          "token" : "과",
          "start_offset" : 3,
          "end_offset" : 4,
          "type" : "word",
          "position" : 2,
          "bytes" : "[ea b3 bc]",
          "leftPOS" : "J(Ending Particle)",
          "morphemes" : null,
          "posType" : "MORPHEME",
          "positionLength" : 1,
          "reading" : null,
          "rightPOS" : "J(Ending Particle)",
          "termFrequency" : 1
        },
        {
          "token" : "백두",
          "start_offset" : 5,
          "end_offset" : 7,
          "type" : "word",
          "position" : 3,
          "bytes" : "[eb b0 b1 eb 91 90]",
          "leftPOS" : "NNG(General Noun)",
          "morphemes" : null,
          "posType" : "MORPHEME",
          "positionLength" : 1,
          "reading" : null,
          "rightPOS" : "NNG(General Noun)",
          "termFrequency" : 1
        },
        {
          "token" : "산",
          "start_offset" : 7,
          "end_offset" : 8,
          "type" : "word",
          "position" : 4,
          "bytes" : "[ec 82 b0]",
          "leftPOS" : "NNG(General Noun)",
          "morphemes" : null,
          "posType" : "MORPHEME",
          "positionLength" : 1,
          "reading" : null,
          "rightPOS" : "NNG(General Noun)",
          "termFrequency" : 1
        },
        {
          "token" : "이",
          "start_offset" : 8,
          "end_offset" : 9,
          "type" : "word",
          "position" : 5,
          "bytes" : "[ec 9d b4]",
          "leftPOS" : "J(Ending Particle)",
          "morphemes" : null,
          "posType" : "MORPHEME",
          "positionLength" : 1,
          "reading" : null,
          "rightPOS" : "J(Ending Particle)",
          "termFrequency" : 1
        }
      ]
    },
    "tokenfilters" : [ ]
  }
}

 

형태소 분석기는 검색에 지대한 영향을 끼치기 때문에 어떤식으로 분해되고 어떤 품사인지에 대해서 알려주는 옵션도 있습니다.

 

📝사용자 사전 (user_dictionary)

PUT my_nori
{
  "settings": {
    "analysis": {
      "tokenizer": {
        "my_nori_tokenizer": {
          "type": "nori_tokenizer",
	  "user_dictionary" : "dictionary/newly_coined_word/newly_coined_word_sungjaefileinc.txt"
        }
      }
    }
  }
}

 

노리 형태소 분석기가 원하는대로 분석이 안 되는 경우 사용자 사전을 추가하여 원하는 방향으로 유도할 수 있습니다.

배열로 하나씩 넣어줄 수 있지만 관리하기도 어려워지고 지저분해지기 때문에 텍스트 파일 즉 사용자 사전을 따로 만들어 관리해주는게 좋습니다.

 

${엘라스틱 설치 위치}/config/기본 root directory 입니다.

 

 

📝불용어 사전 (stopwords)

{
   "settings":{
      "analysis":{
         "filter":{
            "my_stop_filter":{
               "type":"stop",
               "stopwords_path":"dictionary/stopword/stopword_menu.txt"
            }
         },
         "analyzer":{
            "nori_analyzer":{
               "filter":[
                  "my_stop_filter"
               ],
               "tokenizer":"korean_nori_tokenizer"
            }
         },
         "tokenizer":{
            "korean_nori_tokenizer":{
               "type":"nori_tokenizer",
               "user_dictionary":"dictionary/newly_coined_word/newly_coined_word_menu.txt",
               "decompound_mode":"none"
            }
         }
      }
   }
}

 

📝동의어 사전 (synonym)

{
   "settings":{
      "analysis":{
         "filter":{
            "my_synonym":{
               "type":"synonym_graph",
               "synonyms_path":"dictionary/synonym/synonym_menu.txt",
               "lenient":"true"
            }
         },
         "analyzer":{
            "nori_analyzer":{
               "filter":[
                  "my_synonym"
               ],
               "tokenizer":"korean_nori_tokenizer"
            }
         },
         "tokenizer":{
            "korean_nori_tokenizer":{
               "type":"nori_tokenizer",
               "user_dictionary":"dictionary/newly_coined_word/newly_coined_word_menu.txt",
               "decompound_mode":"none"
            }
         }
      }
   }
}

 

type이 synonym_graph인 경우 인덱스를 close 및 open시 즉시 적용이 된다.

 

📝open, close

POST my_nori/_close
POST my_nori/_open

인덱스를 닫을 경우 검색이 안되며 오픈을 해야 검색이 가능하다. (close 후 open시 사용자 사전[신조어] 및 동의어가 바로 적용이 된다) 

반응형
반응형

📝 @Mapper (Mybatis)

@Mapper // XML (쿼리문 내용)을 여기에 DI 하겠다는 의미
public interface DashBoardMapper {
	
	List<LineChartDto> selectLineChartInfo() throws Exception;
	List<PieChartDto> selectEachCount() throws Exception;
	
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.company.ocr.mapper.DashBoardMapper">

	<select id="selectLineChartInfo"
		resultType="com.company.ocr.dto.LineChartDto">
			SELECT
				date_format(a.create_date,'%Y-%m-%d') AS cre_date,
				b.txt_type,
				COUNT(b.txt_type) AS type_count
			FROM 
				ocr_search AS a
					INNER JOIN
				ocr_search_data AS b
				ON a.ocr_no = b.ocr_no
			GROUP BY cre_date, b.txt_type
			ORDER BY cre_date
	</select>
	
	
	<select id="selectEachCount"
		resultType="com.company.ocr.dto.PieChartDto">
		SELECT 
			txt_type,
			COUNT(txt_type) AS type_count
		FROM
			ocr_search_data
		GROUP BY 
			txt_type
	</select>

</mapper>

Mybatis에서 제공해주는 어노테이션으로 XML 지시서 내용을 DI를 하겠다는 의미이다.

 

 

📝 @Component

@Component
public class DashBoardServiceImpl implements DashBoardService{

	@Autowired
	private DashBoardMapper dashBoardMapper; // 객체 생성

	@Override
	public List<LineChartDto> selectLineChartInfo() throws Exception {
		return dashBoardMapper.selectLineChartInfo();
	}

	@Override
	public List<PieChartDto> selectEachCount() throws Exception {
		return dashBoardMapper.selectEachCount();
	}

}

해당 클래스를 빈 객체로 생성한다

 

 

 

📝 @Repository

@Repository 
public class DashBoardServiceImpl implements DashBoardService{

	... 동일

}

DAO 관련 클래스이고 해당 클래스를 빈 객체로 생성한다

 

📝 @Service

@Service 
public class DashBoardServiceImpl implements DashBoardService{

	... 동일

}

비즈니스 로직을 수행하는 클래스이고 해당 클래스를 빈 객체로 생성한다

 

📝 @Controller

@Controller 
public class DashBoardServiceImpl implements DashBoardService{

	... 동일

}

 

스프링 MVC Controller이고 해당 클래스를 빈 객체로 생성한다

 

📝 @Autowired, @Qualifer

@Component
public class DashBoardServiceImpl implements DashBoardService{

	// @Qualifier("exam1") // id를 자료형이 아닌 이름으로 정해주고 싶을 때 사용한다.
	@Autowired
	private DashBoardMapper dashBoardMapper; // IoC컨테이너에 있는 같은 타입 Injection

	@Override
	public List<LineChartDto> selectLineChartInfo() throws Exception {
		return dashBoardMapper.selectLineChartInfo();
	}

	@Override
	public List<PieChartDto> selectEachCount() throws Exception {
		return dashBoardMapper.selectEachCount();
	}

}

Autowired

  • IoC 컨테이너에서 만들어진 빈 객체를 해당 필드에 DI한다 

Qualifier

  • 생성된 빈객체 이름으로 주입한다

우선순위 

    1. 타입 매칭 

    2. 타입 매칭 결과가 2개 이상일 경우 필드 명, 파라미터 명으로 빈 이름 매칭

 

 

📝 @Injection

@Component // Service 내용이 들어있다는 의미
public class DashBoardServiceImpl implements DashBoardService{

	@Inject
	private DashBoardMapper dashBoardMapper; // 객체 생성

	@Override
	public List<LineChartDto> selectLineChartInfo() throws Exception {
		return dashBoardMapper.selectLineChartInfo();
	}

	@Override
	public List<PieChartDto> selectEachCount() throws Exception {
		return dashBoardMapper.selectEachCount();
	}

}

 

Autowired 어노테이션과 유사

 

📝 @Resource

@Component // Service 내용이 들어있다는 의미
public class DashBoardServiceImpl implements DashBoardService{

	// @Resource(name = "propertiesService")
	@Resource
	private DashBoardMapper dashBoardMapper; // 객체 생성

	@Override
	public List<LineChartDto> selectLineChartInfo() throws Exception {
		return dashBoardMapper.selectLineChartInfo();
	}

	@Override
	public List<PieChartDto> selectEachCount() throws Exception {
		return dashBoardMapper.selectEachCount();
	}

}
/**  
Resource 사용하기 위해 필요한 라이브러리
    <dependency>
        <groupId>javax.annotation</groupId>
        <artifactId>javax.annotation-api</artifactId>
        <version>1.3.2</version>
    </dependency>
**/

Autowired 어노테이션과 유사하다. (차이점 : Autowired와 다르게 이름으로 연결해준다.(필드 / 메서드에만 적용 가능))

 

📝 @Value

@Component
public class NewlecExam implements Exam {

	@Value("20")
	int kor;
	@Value("20") // 초기값 설정
	int math;
	
	
	public int getKor() {
		return kor;
	}

	public void setKor(int kor) {
		this.kor = kor;
	}

	public int getMath() {
		return math;
	}

	public void setMath(int math) {
		this.math = math;
	}

}

객체를 생성할 때(Component) 기본 값을 주입한다.

 

📝 @Component, @Configuration, @Bean

// @ComponentScan("di1.ui") // <context:component-scan base-package="di1.ui"/> 을 어노테이션 방식으로 변경
@ComponentScan({"di1.ui","di1"}) // scan 범위가 두개 이상일 경우 
@Configuration // 설정 파일이라는 뜻
public class NewlecDIConfig {
	
	@Bean // 빈 객체를 선언해 IoC 컨테이너에 담아라 라는 어노테이션이다.
	public Exam exam() { // exam → 함수명이 아니라 bean 객체의 id 이름을 의미한다.
		return new NewlecExam();
	}
	
	@Bean // 빈 객체를 선언해 IoC 컨테이너에 담아라 라는 어노테이션이다.
	public ExamConsole console() { // console → 함수명이 아니라 bean 객체의 id 이름을 의미한다.
		return new InlineExamConsole();
	}
}

ComponentScan

  • 해당 경로 하위에 있는 @Componet를 찾아 Bean객체를 생성해 IoC컨테이너에 넣는다

 

Configuration

  • 해당 클래스가 xml 지시서라는 의미이다. (설정 파일)

 

Bean

  • 빈 객체를 생성하라는 의미이다. (Bean 이름 설정 안 할시 함수명으로 생성)

 

📝 @GetMapping  옵션

// 특정 헤더 조건 매핑

@GetMapping(value = "/mapping-header", headers = "mode=debug")
  public String mappingHeader() {
      log.info("mappingHeader");
      return "ok";
}

// **미디어 타입 조건 매핑 - HTTP 요청 Content-Type, consume**

@PostMapping(value = "/mapping-consume", consumes = "application/json")
  public String mappingConsumes() {
      log.info("mappingConsumes");
      return "ok";
}

// 미디어 타입 조건 매핑 - HTTP 요청 Accept, produce
@PostMapping(value = "/mapping-produce", produces = "text/html")
  public String mappingProduces() {
      log.info("mappingProduces");
      return "ok";
}

 

📝 @PostMapping, @PatchMapping, @DeleteMapping

// 일반적으로 회원 목록 조회
@GetMapping(value = "/users")
public User getUsers(){
    ....
}

// 일반적으로 회원 등록
@PostMapping(value = "/user")
public User signupUser(){
    ....
}

// 일반적으로 회원 수정
@PatchMapping(value = "/user")
public User updateUser(){
    ....
}

// 일반적으로 회원 삭데
@DeleteMapping(value = "/user")
public User deleteUser(){
    ....
}

 

 

 

 

반응형
반응형

📝인덱스 삭제

DELETE board
# board 인덱스 삭제

 

📝레코드 추가

POST board/_doc/1 
{
  "title" : "한국항공우주산업 너무 재미있어요."
}
# board 인덱스 1번 레코드의 title필드에 값 추가

 

📝인덱스 레코드 조회

GET board/_search
{
  "query": {
    "match": {
      "title": {
        "query": "한국"
      }
    }
  }
}
# board 인덱스에 title 필드에 "한국" 포함되는 조건 조회

 

📝분석기를 이용한 텍스트 형태소 분석 결과 조회

GET board/_analyze
{
  "analyzer": "nori",
  "text": "한국항공우주산업"  
}
# board 인덱스에 nori 분석기를 사용한 형태소 분해 결과 조회 (여기서 nori는 노리형태소분석기)
# 형태소 분해 결과와 일치하는 단어로 검색해야 검색에 노출된다.

GET board/_analyze
{
  "analyzer": "nori",
  "text": "한국항공우주산업",
  "explain": true
}
# explain : nori 분석기를 이용해서 나오는 모든 형태소 분해 결과를 상세히 보여준다 (품사 종류 등...)
# stoptags filter 적용이 안 된 상태로 다 보여준다.

 

📝인덱스 생성

PUT board
{
  "mappings" : {
    "properties" : {
      "category" : {
        "type" : "text"
      },
      "company" : {
        "type" : "text"
      },
      "contents" : {
        "type" : "text",
        "analyzer": "nori"
      },
      "del_yn" : {
        "type" : "text"
      },
      "file_path" : {
        "type" : "text"
      },
      "file_contents" : {
        "type" : "text"
      },
      "id" : {
        "type" : "integer"
      },
      "modi_date" : {
        "type" : "date"
      },
      "reg_date" : {
        "type" : "text"
      },
      "title" : {
        "type" : "text"
      },
      "writer" : {
        "type" : "text"
      }
    }
  },
  "settings" : {
    "index" : {
      "analysis" : {
        "filter" : {
          "nori_posfilter" : {
            "type" : "nori_part_of_speech",
            "stoptags" : [
              "J",
              "SP",
              "VSV",
              "UNKNOWN",
              "UNA",
              "SSO",
              "SSC",
              "SP",
              "SH",
              "SF",
              "SE",
              "SC",
              "NA",
              "E",
              "NNB",
              "XSV",
              "VV",
              "XSA",
              "VA",
              "VX",
              "VCP"
            ]
          }
        },
        "analyzer" : {
          "nori" : {
            "filter" : [
              "nori_posfilter"
            ],
            "tokenizer" : "korean_nori_tokenizer"
          }
        },
        "tokenizer" : {
          "korean_nori_tokenizer" : {
            "type" : "nori_tokenizer",
            "decompound_level" : "mixed",
            "user_dictionary" : "dictionary/newly_coined_word/newly_coined_word_sungjaefileinc.txt"
          }
        }
      }
    }
  }
}

|―mapping

|settings

        |index

        |analysis

                  |filter

                  | analyzer

                  | tokenizer

 

📝매핑

"mappings" : {
"properties" : {
  "category" : {
    "type" : "text"
  },
  "company" : {
    "type" : "text"
  },
  "contents" : {
    "type" : "text",
    "analyzer": "nori"
  },
  "del_yn" : {
    "type" : "text"
  },
  "file_path" : {
    "type" : "text"
  },
  "file_contents" : {
    "type" : "text"
  },
  "id" : {
    "type" : "integer"
  },
  "modi_date" : {
    "type" : "date"
  },
  "reg_date" : {
    "type" : "text"
  },
  "title" : {
    "type" : "text"
  },
  "writer" : {
    "type" : "text"
  }
}

# 키 이름은 category 이고 type은 text (전문)이다.
# ....
# 키 이름은 id 이고 type 은 integer(정수)이다.

 

📝필터

"filter" : {
  "nori_posfilter" : {
    "type" : "nori_part_of_speech",
    "stoptags" : [
      "J",
      "SP",
      "VSV",
      "UNKNOWN",
      "UNA",
      "SSO",
      "SSC",
      "SP",
      "SH",
      "SF",
      "SE",
      "SC",
      "NA",
      "E",
      "NNB",
      "XSV",
      "VV",
      "XSA",
      "VA",
      "VX",
      "VCP"
    ]
  }
}
# nori_posfilter는 filter 이름
# nori_part_of_speech은 nori에서 제공하는 필터로 품사에 대한 제외어(불용어)처리가 가능하다

 

📝분석기

"analyzer" : {
  "nori" : {
    "filter" : [
      "nori_posfilter"
    ],
    "tokenizer" : "korean_nori_tokenizer"
  }
}
# analyzer는 형태소 분석기를 선언한다.
# nori라는 이름이고 filter는 위에 설명한 nori_posfilter를 이용한다.
# tokenizer로는 korean_nori_tokenizer 이름의 분석기를 사용한다 (여기에선 노리형태소분석기)

 

📝형태소 분석기

"tokenizer" : {
  "korean_nori_tokenizer" : {
    "type" : "nori_tokenizer",
    "decompound_level" : "mixed",
    "user_dictionary" : "dictionary/newly_coined_word/newly_coined_word_sungjaefileinc.txt"
  }
}
# tokenizer는 형태소 분석기를 의미하며 korean_nori_toknenizer라는 이름이다.
# nori_tokenizer는 노리 형태소 분석기를 의미한다.
# decompound_level은 형태소 분해 방법이다.
# user_dictionary는 사용자 사전이다. (신조어 사전)

 

 

반응형
반응형

📝Promise

비동기 처리를 도와주는 객체로서 콜백지옥의 문제를 깔끔하게 해결해줄 수 있다 → Promise Chainning 기술을 이용
물론 남발하면 콜백 지옥이 다시 시작된다 → 잘 활용 해야한다

 

 

Promise 상태

  • Pending
    • 수행이 끝나지 않은 상태 → resolve 값이 반환이 안 된 상태
  • Reject
    • 수행을 시켰지만 실패된 상태
  • Fulfilled
    • 수행이 올바르게 끝난 상태

 

Promise 안에 비동기(일반적으로 Ajax, Fetch, Axios 등...)에 대한 내용 및 성공했을 때 반환할 내용인 Resolve와 실패했을 때 반환할 내용인 Reject가 들어가며 Then()을 이용해 비동기 내용을 실행시킨다  → Promise 선언시 바로 동작 되기 때문에 Function안에 사용하는게 일반적이다.

 

 

Promise 사용 방법 (기초)

// Promise (Async operation)
//  - get status (pending → fufilled or rejected)
//  - Producer, Consumer


/** ───── 1. Producer (Promise 객체는 선언시에 자동적으로 실행된다.) ───── **/
const promise = new Promise((resovle, reject) => { // 기본 문법 new Promise((resolve, reject) => ...
    console.log('doing something...');             // doing some heavy work
    // setTimeout(() => resovle('ellie'), 2000)    // 성공시에는 ellie라는 값 return
    setTimeout(() => reject(new Error('no network')), 2000) // 실패시 no network라는 값 return                                                            
});



/** ───── 2. Consumer (then, catch, finally) ───── **/
 promise.then(value => console.log(value))     // 성공시 ellie 출력 (then은 promise을 실행한다를 의미)
        .catch(error => console.log(error))    // 실패시 no network 출력 (Error 핸들링은 catch 사용)
        .finally(() => console.log('finally')) // 성공하든 실패하든 finally 출력 



/** ───── 3. Promise chaining ───── **/
const fetchNumber = new Promise((resolve, reject) => {
    setTimeout(() => resolve(1), 1000); // return 값 : 1
});

fetchNumber
    .then((num) => num * 2) // return 값 1을 파라미터로 1 * 2 값을 return (resolve)
    .then((num) => num * 3) 
    .then((num) => {
        return new Promise((resovle, reject) => 
             setTimeout(() => resovle(num -1), 1000)) ;
    }) // promise를 return 가능
    .then((num) => console.log(num)); // then 을 쓰려면 resolve가 있어야한다. (then 쓰고 핸들링이 있을 경우인 거 같다)

 

 

콜백지옥 (URL 통신)

/**
    @author 이성재
    @why    엔진 기동
 */
let turnOn = () => {
	
	if (confirm("검색 엔진을 기동하시겠습니까?") == true) {
		
        // 패스워드 체크     
        let adminPw = $("#admin_pw").val();
        let id = "testid";
        let shaAdminPw = hex_sha512(adminPw).toString();
        return new Promise((resolve, reject) => {
            $.ajax({
            type: "GET",
            url: "./check/password", // hecking
            data: {
                shaAdminPw: shaAdminPw, // 암호화 password
                id: id, // id
                type: 'f' // ?
            },
            success: (data) => {
                if (data !== "success") return "비밀번호가 틀렸습니다";
                
                if (data === "success") {

                    // 검색엔진 기동
                    $.ajax({
                        url: './turn-on',
                        type: 'GET',
                        success: () => { return "검색 엔진이 기동되었습니다."},
                        error:   () => { return "통신 오류가 발생했습니다."}
                    }); // ajax end	

                }
            },
            error : () => { return "패스워드 체크에 실패했습니다.";}
        }); // ajax 종료
        });
	}else {
		return false;
	}
}

 

 

Ajax Promise Chaning으로 콜백 지옥 해결 (URL 통신)

/**
    @author 이성재
    @why    엔진 기동 요청
 */
let turnOnAjax = () => {
	return new Promise((resolve,reject) => {
	$.ajax({
		url: './turn-on',
		type: 'GET',
		success: () => {resolve("검색 엔진이 기동되었습니다.")},
		error:   () => {reject("통신 오류가 발생했습니다.")}
	}); // ajax end	
	}); // promise end
}

/**
    @author 이성재
    @why    엔진 재기동에 필요한 패스워드 검사
 */

let checkPasswordAjax = () =>{
	
	let adminPw = $("#admin_pw").val();
	let id = "testid";
	let shaAdminPw = hex_sha512(adminPw).toString();
	return new Promise((resolve, reject) => {
		$.ajax({
		type: "GET",
		url: "./check/password", // hecking
		data: {
			shaAdminPw: shaAdminPw, // 암호화 password
			id: id, // id
			type: 'f' // ?
		},
		success: (data) => {
			if (data == "success") resolve("success");
			else resolve("비밀번호가 틀렸습니다");
		},
		error : () => {reject("패스워드 체크에 실패했습니다.");
		}
	}); // ajax 종료
	});
}


/**
    @author 이성재
    @why    엔진 기동
 */
let turnOn = () => {
	
	if (confirm("검색 엔진을 기동하시겠습니까?") == true) {
		
		// 패스워드 체크
		checkPasswordAjax()
		.then((message) => {
			if(message === "success") return turnOnAjax();
			else alert(message); // 패스워드 틀림
		})
		.catch((errorMsg) =>{
			alert(errorMsg);
		}) 
		// 엔진 기동
		.then((message) => {
			g_showLayer();
			alert(message);
			location.reload()	
		})
		.catch((errorMsg) =>{
			alert(errorMsg);
		})
		.finally(() => g_closeLoading)
		
	}else {
		return false;
	}
	
}

 

 

📝Async

Promise는 비동기 처리를 좀 더 가독성 및 여러가지 측면에서 효율적으로 만들어주지만 그에 대한 코드가 너무 길어서 지저분에 보일 수 있다 Async는 이를 보완해준다 Async 키워드는 Function 앞에 사용하며 해당 함수는 항상 프라미스를 반환된다

 

Asyn 기본 예제

// 1. async
async function fetchUser() {
    return 'ellie'; // async에서 return = resolve와 등치
}

const user = fetchUser(); 
user.then(console.log);   // Promise {[[PromiseState]]: 'fulfilled', [[PromiseResult]]: 'ellie', Symbol(async_id_symbol): 5, Symbol(trigger_async_id_symbol): 1}
console.log(user);        // ellie

 

Async 기본예제2

function delay(ms){
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function getApple(){
    await delay(1000);
    return 'apple';
}

async function getBanana(){
    await delay(1000);
    return 'banana';
}

function getBananaPromise(){ // getBanana Promise Version (getBanana()와 등치)
    return delay(1000)
    .then (()=>'bananaPromise')
}

let apple = getApple();  // promise pending 상태 
apple.then(console.log); // apple

getBanana().then(console.log) // banana
getBananaPromise().then(console.log); // bananaPromise

 

 

📝Await

비동기 통신이 순차적으로 이루어져야하는 경우 또는 비동기 통신한 값을 변수에 담고 싶은 경우 사용할 수 있다.

Await는 말 그대로 통신을 기다리는 것이고 Async 안에서만 사용이 가능하다 → Await는 Then()을 포함해 실행시킨다

 

 

Await 예제

function delay(ms){
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function getApple(){
    await delay(1000);
    return 'apple';
}

async function getBanana(){
    await delay(1000);
    return 'banana';
}

async function pickFruits(){ 
    const apple = await getApple();   // 1초 
    const banana = await getBanana(); // 1초
    return `${apple} + ${banana}`     
} // Total : 2초

pickFruits().then(console.log) // apple + banana

 

위와 같은 경우는 실행시 getApple()로 비동기 객체(Promise)를 가져오면서 실행이 됩니다.

Await의 역할Promise 상태가 fufilled가 될때까지 기다립니다 그리고 그 결과 값을 변수에 할당해줄 수 있습니다.

 

  • getApple을 통해 Promise를 가져오고 (pending 상태) await을 거쳐 1초 기다리고 Apple을 반환
  • getBanana을 통해 Promise를 가져오고 (pending 상태) await을 거쳐 1초 기다리고 Banana를 반환

총 2초가 걸리게 됩니다 이렇게 할 경우 순차적으로 처리가 가능하지만 병렬로 처리가 안 되기 때문에 낭비되는 시간이 생깁니다 그럴 경우 이런식으로 처리가 가능합니다. 

 

Await 개선

function delay(ms){
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function getApple(){
    await delay(1000);
    return 'apple';
}

async function getBanana(){
    await delay(1000);
    return 'banana';
}

async function pickAllFruits(){
    const applePromise = getApple();   // promise 객체 (pending)
    const bananaPromise = getBanana(); // promise 객체 (pending)
    const apple = await applePromise   // 실행 후 대기
    const banana = await bananaPromise // 실행 후 대기
    return `${apple} + ${banana}`
}

pickAllFruits().then(console.log) // apple + banana
  • applePromise = getApple()
  • bananaPromise = getBanana()

 

이 두개를 통해 각각 비동기로 먼저 호출해서 결과값을 가진 (fulfilled) Promise 를 만들도록 한 뒤 await을 통해 fullfilled 가 된 경우 그 결과값이 apple과 banana로 들어가게 됩니다 그래서 1초만에 apple + banana가 출력이 되게 됩니다.

 

 

 

📝Promise.all

Await개선에 있는 코드처럼 쓰는 경우 뭔가 중복된 느낌이 강합니다. 이 부분을 개선한 방식이 Promise.all입니다.

Promise.all의 경우 all에 등록한 Promise객체가 전부 완료될때까지 기다려줍니다

 

가독성 해치는 코드

// 가독성을 해침
async function pickAllFruits(){
    const applePromise = getApple();   // promise 객체 (pending)
    const bananaPromise = getBanana(); // promise 객체 (pending)
    const apple = await applePromise   // 실행 후 대기
    const banana = await bananaPromise // 실행 후 대기
    return `${apple} + ${banana}`
}

 

Promise.all로 개선

function delay(ms){
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function getApple(){
    await delay(1000);
    return 'apple';
}


async function getBanana(){
    await delay(1000);
    return 'banana';
}


function pickFruits(){
     return Promise.all([getApple(),getBanana()]) // promise 객체
     .then(fruits => fruits.join(' + '));
}

pickFruits().then(console.log); // apple + banana

 

 

📝Promise.race

race는 말 그대로 경주를 한다는 의미입니다. 등록된 Promise 객체중에서 먼저 종료되는 Promise만 리턴해줍니다.

 

Promise.race

function delay(ms){
    return new Promise(resolve => setTimeout(resolve, ms));
}

async function getBananaOne(){
    await delay(1000);
    return 'banana';
}

async function getBananaTwo(){
    await delay(2000);
    return 'banana2';
}

async function getAppleOne(){
    await delay(1000);
    return 'apple';
}

async function getAppleTwo(){
    await delay(2000);
    return 'apple2';
}

function pickOnlyOne1() {
    return Promise.race([getAppleTwo(), getBananaOne()]);
}

function pickOnlyOne2() {
    return Promise.race([getAppleOne(), getBananaTwo()]);
}

pickOnlyOne1().then(console.log); // banana
pickOnlyOne2().then(console.log); // apple

 

 

Arrow Function을 이용한 async 표현법

// Arrow Function을 이용한 async 표현법
const getDog = async () => {
    await sleep(1000);
    return '멍멍이';
};

 

fetch을 이용해 json 데이터 받아오기

const fetching = fetch('https://dummyjson.com/products/1'); // fetch에 대한 내용이 들어간 Promise
const response = await fetching; // Promise 실행시킨 Response 객체 내용
// response.then()과 동일한 역할이지만 데이터를 받아온 후에 처리하려면 response.then((res)=>res.json()) 이런식 처리 필요
const data = await response.json(); // response에서 응답값 결과

 

 

🔗 참고 및 출처

https://www.youtube.com/watch?v=aoQSOZfz3vQ&t=736s

반응형
반응형
// Sync → 순차적인 실행
function sync(){
    console.log('sync callback');
    console.log('1');
    console.log('2');
    console.log('3');
    // 1 → 2 → 3
}

// Async → 순차적이지 않은 실행 (분산 처리로 속도가 빠르지만 언제 값이 내려올지 몰라 대응하기 어려움)
function async(){
    console.log('sync callback');
    console.log('1');
    setTimeout(() => console.log('2'), 1000);
    console.log('3');
    // 1 → 3 → 2
}

sync();
async();

 

 

// Callback Hell (Callback Method call Callback Method) 
// → callback 함수를 남발하여 안에 내용이 보기 힘들어진 것
class UserStorage {
    loginUser(id, password, onSuccess, onError){
        setTimeout(() => {
            if (id === 'ellie' && password === 'dream')
                onSuccess(id); // id 파라미터 값을 넘겨준다.
            else
                onError(new Error('not found')); // not found라는 에러메세지를 넘겨준다
        }, 2000);
    }

    getRoles(user, onSuccess, onError){
        setTimeout(() => {
            if (user === 'ellie')
                onSuccess()
            else
                onError(new Error('no access'));
        }, 1000);
    }
}

const userStorage = new UserStorage();
const id = 'ellie';
const password = 'dream';
userStorage.loginUser(
    id,
    password,
    success => {
        userStorage.getRoles(
            id, 
            success => console.log(`Hello ${id}`) ,
            error => console.log(error)
        )
    },
    error => console.log(error)
)

// id가 ellie 이고 password가 dream인 걸 서버에서 확인(서버 통신시간 2초라고 가정)
// 그 이후에 올바른 경우 onSuccess가 작동하고 그렇지 않으면 onError가 작동한다.
반응형
반응형
// XHR (데이터 요청과 받아올 수 있는 Object)
// XMLHttpRequest로 맨 처음 개발 당시에는 XML을 이용했지만 추후에는 여러 데이터 format으로 사용 가능
// 이름만 XML인 거지 다양한 format 사용 가능하다.
// 문자열 → Object (Serialize) 
// Object → 문자열 (Deserialize)

const rabbit = {
    name : 'tomi',
    color : 'white',
    size : 'small'
}

// JSON.stringify = String to JsonObject
let json = JSON.stringify(rabbit); // {"name":"tomi","color":"white","size":"small"}
console.log(json);

// 원하는 키만 JSON 변환 가능
json = JSON.stringify(rabbit, ['name']); 
console.log(json); // {"name":"tomi"}

// jsonObject의 forEach
json = JSON.stringify(rabbit, (key, value) => {
    console.log(`key : ${key}, value : ${value}`);
    return value; // 필수
   // return key === 'name'? 'tomi' : value; // key가 name인 경우 tomi를 넣어 return 해준다.
});

// key : , value : [object Object] // 최상위
// key : name, value : tomi
// key : color, value : white
// key : size, value : small


// jsonObject to String
const obj = JSON.parse(json);
console.log(`obj : ${obj}`) // {"name":"tomi","color":"white","size":"small"}
console.log(`name : ${obj.name}`); // name : tomi


const obj2 = JSON.parse(json, (key, value) =>{
    console.log(`key : ${key}, value : ${value}`);
    return value; 
});

// key : name, value : tomi
// key : color, value : white
// key : size, value : small
// key : , value : [object Object]
반응형
반응형

 

  • 초기값 데이터 선언
class Student {
    constructor(name, age, score){
        this.name = name;
        this.age = age;
        this.score = score;
    }
}

const students = [
    new Student('Sam', 40, 90),
    new Student('Amy', 20, 80),
    new Student('Tom', 30, 100),
    new Student('Carrot', 45, 70),
    new Student('Julia', 15, 75)
];

 

📝filter

// filter (나이가 20 이상인 사람만 출력한다.)
const oldStudents = students.filter((student) => student.age > 20);
oldStudents.forEach((student) => console.log(student));
// Student {name: 'Sam', age: 40, score: 90}
// Student {name: 'Tom', age: 30, score: 100}
// Student {name: 'Carrot', age: 45, score: 70}

필터는 말 그대로 걸러내는 작업으로 배열을 하나씩 순회하면서 조건에 맞는 것(true)만 배열에 담습니다

 

📝map

// map (모든 student score에 2를 곱한다)
const studentScores = students.map((student) => student.score * 2);
console.log(studentScores); // [180, 160, 200, 140, 150]

map의 경우 배열을 하나씩 순회하면서 각 요소에 동일하게 적용시킬 수 있습니다 예를 들면 각 배열의 숫자를 2배를 한다 등의 행동을 할 수 있습니다. forEach랑 다른 점은 새로운 배열을 반환시킬 수 있습니다 [코드 길이 감소]

 

📝some, every

// some (하나라도 만족하는 경우)
{
    const result = students.some((student) => student.score < 80);  
    console.log(result); // true 
}

// every (모두다 만족하는 경우)
{
    const result = students.every((student) => student.score < 80);  
    console.log(result); // false
}
  • some의 경우 하나라도 만족하면 true를 보내며 나머지 배열을 순회 안 하고 끝냅니다
  • every의 경우 모든게 다 만족해야 true를 보냅니다 그렇지 않으면 false를 반환합니다

 

📝reduce

// reduce (totalScore : 이전 값 ,  curr : 현재 값)
{
    const result = students.reduce((totalScore, curr) => {
        console.log(totalScore); // 0, 90, 170, 270, 340
        console.log(curr); 
        // Student {name: 'Sam', age: 40, score: 90}
        // Student {name: 'Amy', age: 20, score: 80}
        // Student {name: 'Tom', age: 30, score: 100}
        // Student {name: 'Carrot', age: 45, score: 70}
        // Student {name: 'Julia', age: 15, score: 75}

        return totalScore + curr.score; // return 값이 다음 loop의 totalScore에 들어가게 된다.
    }, 0); // 0은 초기값 
}

reduce의 경우 map하고 비슷하게 보이지만 map의 경우 앞에 있는 인덱스가 뒤에 있는 인덱스에 영향을 끼치지 않습니다 reduce의 경우 앞에 값이 뒤에 영향을 줄 수 있습니다 예를들면 모든 배열의 값을 더한다고 했을 때 0번째 인덱스와 1번째 인덱스와 더한값을 다음 배열 인덱스[2번째]가 받아서 더하고 계속적으로 반복합니다 거의 계산(합, 차 등...)하거나 할 때 많이 쓰입니다

 

 

 

 

반응형
반응형

📝Arrays, of, forEach

// Arrays
const foods = ['🍗','🍟','🍔'];
for (let indexNum = 0; indexNum < foods.length; indexNum++){
    console.log(foods[indexNum]);
}

// for of
for (let food of foods){
    console.log(food);
}

// for in
let obj = {
  a: 1,
  b: 2,
  c: 3
};

for (let item in obj) {
    console.log(item); // a b c
    console.log(obj[item]); // 1 2 3
}


/** 에러 발생 */
// for (let item of obj) {
//     console.log(item);
// }

// forEach
foods.forEach(function (food, index){
    console.log(food, index);
});

foods.forEach((food, index) => console.log(food, index));

for of의 경우 반복가능한 객체 (Array, Map, Set, String, TypedArray, arguments 객체 등을 포함)에 대해서 반복한다

for in의 경우는 객체에 사용이 가능하다

 

 

🔗 참고 및 출처

https://doozi0316.tistory.com/entry/JavaScript-for-in-for-of%EC%9D%98-%EC%B0%A8%EC%9D%B4

반응형
반응형

📝Class 클래스

// Class

class Person {
    // constructor
    constructor(name, age){
        this.name = name;
        this.age = age;
    }

    // methods
    speak(contents){
        console.log(`${this.name} : ${contents}`);
    }

    // this.age라고 쓴 경우 get age()를 호출
    get age(){
        // return this.age; 이렇게 사용시 get age()를 계속 호출
        return this._age;
    }

    // this.age에 값을 설정할 때 set age()를 호출
    set age(value){
        // this.age = value; 이렇게 사용시 set age()를 계속
        this._age = value < 0 ? 0 : value;
    }
}

const sam = new Person('Sam', 20);
console.log(sam.name);
console.log(sam.age);
sam.speak('Hello!');

JavaScript에서 클래스를 위와 같이 사용할 수 있다

 

📝Extends, Overrride

class Shape {
    
    constructor(width, height, color){
        this.width = width;
        this.height = height;
        this.color = color;
    }

    paint(){
        console.log(`painting ${this.color} color`)
    }

    draw(){
        console.log(`drawing shape`);
    }
}

class Rectangle extends Shape {
    // Overriding
    draw(){
        // super.draw(); 부모 함수 사용 가능
        console.log(`drawing Rectangle`)
    }
}

const rectangle = new Rectangle(100, 200, 'red');
rectangle.paint();
rectangle.draw();

Extends를 이용해 Class를 상속받을 수 있고 Override처럼 자식 클래스가 함수를 재정의해서 사용할 수도 있다.

 

반응형