기준이 되는 수를 정하고 그 수와 나머지를 비교하여 가장 낮은 수를 제일 앞으로 보내는 정렬입니다.

 

코드프레소 DevOps Roasting Course 2기 진행중입니다.

 

'자료구조와 알고리즘' 카테고리의 다른 글

백준 2439번 별 찍기2  (0) 2020.05.15
백준 2438번 별 찍기  (0) 2020.05.06
백준 11021번 A+B  (0) 2020.04.30
백준 2742번 기찍 N  (0) 2020.04.30
백준 2741번 N 찍기  (0) 2020.04.28

현재 Vue를 사용하여 간단한 회원, 상품 관련 서비스 쇼핑몰 사이트를 만들고 있다.

회원가입을 하고 로그인을 하면 토큰이 생성되도록 백엔드쪽에 API를 만들어 놓았다.

그 토큰을 쿠키로 사용하여 로그인 시 Sign Out 버튼과 My Basket 버튼이 보이고, 로그아웃 시 Sign Up 버튼과 Sign In 버튼이 생기도록 화면을 구현해보고자 한다.

목차

1. 라이브러리 설치

2. 쿠키 생성 - main.js

3. 쿠키 생성 - SignIn.vue

4. 쿠키 삭제

1. 라이브러리 설치

https://www.npmjs.com/package/vue-cookie

 

vue-cookie

A Vue.js plugin for manipulating cookies

www.npmjs.com

위 사이트에 가면 vue-cookie 라이브러리를 사용하는 법이 아주 친절하게 나와 있다.

터미널 창에 다음과 같이 입력하여 vue-cookie 라이브러리를 설치한다. 참고로 vue-cookies 라이브러리도 있는데, 거의 비슷하다.

package.json 파일의 dependencies를 살펴 보면 vue-cookie가 설치된 것을 확인할 수 있다.

쿠키를 생성하고 삭제하는 방법은 위 사이트에 예시로 설명이 아주 잘 되어 있다.

 

 

음..ㅎ 지금은 대충 이런 상태.

F12를 눌러서 Application > Cookies에 가면 아직 아무런 데이터가 없다. 물론 token도 없기 때문에 화면에 버튼은 Sign Up, Sign In이 보인다.

 

2. 쿠키 생성 - main.js

쿠키를 생성하기에 앞서 main.js에서 vue-cookie를 import해 준다.

 

3. 쿠키 생성 - SignIn.vue

다음과 같이 SignIn.vue를 작성한다. 이메일과 비밀번호를 입력하고 버튼을 누르면 토큰이 생성됨과 동시에 홈 화면으로 전환되고 헤더 부분의 버튼들이 바뀌는 형식이다. 

axios를 통해 서버와 post방식으로 통신하여 이메일과 비밀번호 값을 서버에 보내면 해당 사용자의 토큰 데이터를 받아올 수 있다.

그러면 40번째 줄과 같이 토큰을 accesstoken이라는 키로 쿠키에 저장할 수 있다.

쨘! 로그인을 하면 쿠키에 accesstoken이라는 이름으로 토큰값이 들어간 것을 확인할 수 있다.

또한 보여지는 화면에도 회원가입, 로그인 버튼은 사라지고 로그아웃, 내 장바구니 버튼으로 전환되었다.

이와 같은 로직이 필요한 이유는 로그인을 해야만 내 장바구니에 들어갈 수 있도록 해야 하기 때문이다.

 

4. 쿠키 삭제

쿠키를 삭제하는 법도 간단하다.

쿠키를 삭제하는 기능은 로그인 창에서 이루어지는 게 아니라 헤더에 있는 로그아웃 버튼을 눌렀을 때 작동해야 하기 때문에 AppHeader.vue 파일에 코드를 작성하였다.

42번째 줄에 간단히 쿠키를 삭제하는 코드를 작성한다.

또한 v-if 구문을 사용하여 버튼이 변경되도록 하였다.

로그아웃 버튼을 누르면 다음과 같이 쿠키가 정상적으로 삭제되고 화면의 버튼들도 바뀌었음을 확인할 수 있다.

 

 

코드프레소 DevOps Roasting 코스 2기 진행 중입니다.

지난 번까지는 각 테이블 간의 연관관계를 하나도 지정해주지 않고 기본적인 작동 방식만 알아보았다. 이번에는 PK-FK로 연관된 테이블의 관계를 엔티티에서 역시 맺어보고자 한다.

이 프로젝트의 테이블들의 상관관계는 다음과 같다.

상품과 관련된 prod 테이블이 있고, 상품을 조회하면 글/이미지 쌍으로 디테일이 있는 prod_detail 테이블이 있다. 두 테이블은 1(prod) : N(prod_detail) 관계이다. prod 테이블의 no 컬럼이 PK이며, prod_detail의 prod_no 컬럼이 prod 테이블을 참조하는 컬럼이다. 상품이 삭제되면 디테일도 삭제되어야 하기 때문에 양방향 관계를 가진다.

이 방향이라는 것은, 양방향/ 단방향이 있다.

테이블 개념에서 볼 때 PK-FK로 연관관계를 맺고 있으면 방향이랄 것이 없다.

하지만 JPA 환경에서 엔티티들은 기본적으로 단방향이다. Prod 엔티티에 @OneToMany 어노테이션으로 ProdDetail을 명시해주면 Prod->ProdDetail의 방향이 된다. 테이블의 양방향 상태처럼 만들어주려면 ProdDetail 엔티티에서도 @ManyToOne 설정을 해 주어야 한다.

 

@Getter
@Setter
@ToString
@Table(name = "prod")
@Entity
public class Prod {

	@Id
	@GeneratedValue
	@Column(name = "no")
	private Long no;
	private String name;
	private String thumbnailUrl;
	private Long originPrice;
	private Long discPrice;
	private String description;
	private LocalDateTime createdAt;
	@Transient
	private boolean inBasket;

	@PrePersist
	public void createdAt() {
		this.createdAt = LocalDateTime.now();
	}

	@OneToMany(mappedBy = "prod", fetch = FetchType.EAGER)
	private List<ProdDetail> detailList = new ArrayList<ProdDetail>();
}
@Getter
@Setter
@ToString
@Table(name = "prod_detail")
@Entity
public class ProdDetail {

	@Id
	private Long id;
//	@Column(name = "prod_no")
//	private Long prodNo;
	private String content;
	private String imageUrl;
	private LocalDateTime createdAt;

	@PrePersist
	public void createdAt() {
		this.createdAt = LocalDateTime.now();
	}

	@ManyToOne
	@JoinColumn(name = "prod_no", nullable = false, updatable = false)
	@JsonIgnore
	private Prod prod;

	public void setProd(Prod prod) {
		if (this.prod != null) {
			this.prod.getDetailList().add(this);
		}
		this.prod = prod;
		prod.getDetailList().add(this);
	}

}

@OneToMany의 mappedBy 속성은 양방향 매핑할 때 사용하는데 반대쪽 매핑의 필드 이름을 값으로 준다. 이렇게 되면 서로 참조할 수 있는 구조를 가진다.

1:N에서 N입장인 ProdDetail에서 @JoinColumn을 명시해주어야 연관관계를 관리하는 조인 테이블 전략을 기본으로 사용해서 매핑한다. 반드시 name 속성에 참조하는 테이블(prod)_기본키(no) 형태로 적어주어야 한다.

이렇게 하면 양방향 관계 설정이 된다.

 

상품을 조회할 때 상품과 디테일의 글/이미지 모두를 조회하는 API를 만들어보고자 한다.

@RestController
@RequestMapping("/prod")
public class ProdController {

	private static final Logger logger = LoggerFactory.getLogger(ProdController.class);

	@Autowired
	ProdService prodService;

	@GetMapping("/{id}")
	public ResponseVO findProdWithDetailByUser(@CookieValue(value = "accesstoken", required = false) String accesstoken,
			@PathVariable("id") Long no) throws Exception {
		logger.info("call findProdWithDetailByUser()");
		
		Optional<Prod> prodResult = prodService.findProdWithDetailByUser(accesstoken, no);
		ResponseVO result = new ResponseVO();
		result.setCode(HttpStatus.OK);
		result.setMessage("SUCCESS");
		result.setData(prodResult);
		return result;
	}	
}
@Service
public class ProdService {

	private static final Logger logger = LoggerFactory.getLogger(ProdService.class);

	@Autowired
	TokenRepository tokenRepo;

	@Autowired
	ProdRepository prodRepo;

	@Autowired
	BasketRepository basketRepo;

	public Optional<Prod> findProdWithDetailByUser(String accesstoken, Long no) {
		logger.info("call findProdWithDetailByUser()");

		Optional<Prod> prodResult = prodRepo.findById(no);

		Token token = tokenRepo.findByToken(accesstoken);
		String email = token.getUserEmail();
		Basket basket = basketRepo.findByUserEmailAndProdNo(email, no);
		if (basket != null) {
			boolean inBasket = true;
			prodResult.get().setInBasket(inBasket);
			return prodResult;
		} else {
			return prodResult;
		}
	}

}

(로그인했을 시 장바구니에 담겨 있는지 없는지 여부를 확인하는 로직도 있다.)

findById 메소드를 사용할 것이기 때문에 레포지토리에 따로 메소드를 생성할 필요는 없다.

 

포스트맨을 사용해서 1번 상품을 조회한다.

성공적으로 상품을 조회한다.

콘솔을 확인하면, findById 메소드가 실행되었을 때 left outer join으로 prod_detail의 데이터까지 조회한 것을 확인할 수 있다.

 

@OneToMany, @ManyToOne 등의 설정은 JPA로 테이블 간의 관계 설정을 보다 손쉽게 해 주는  또 하나의 특징적인 기능이라는 것을 이번 프로젝트 때 알게 되었다.

참조한 블로그

목차

1. DAO, Service, Controller 작성

2. REST API를 사용하여 회원가입 기능 구현

이번에는 간단히 CRUD 기능을 사용하기 위해 DAO, Service, Controller를 작성하는 것에 대해 알아보고자 한다.

 

1. DAO, Service, Controller 작성

MyBatis 환경에서 DAO와 동일한 개념이 레포지토리 인터페이스이다. 여기서 JPA의 특징적인 점은 별도의 구현 클래스를 만들지 않고 인터페이스만 정의함으로써 기능을 사용할 수 있다는 것이다.

여기서 보통 Spring Data 모듈에서 제공하는 CrudRepository를 상속한다. 추가적으로 페이징 처리를 할 경우에는 PagingAndSortingRepository를 사용한다.

package com.codepresso.persistence;

import org.springframework.data.repository.CrudRepository;

import com.codepresso.domain.User;

public interface UserRepository extends CrudRepository<User, Long> {

}

 

이렇게 간단한 인터페이스만으로도 데이터 입력이 잘 되는지 보기 위해 간단히 컨트롤러와 서비스를 작성한다.

@RestController
@RequestMapping("/user")
public class UserController {
	
	@Autowired
	private UserService userService;
	
	@PostMapping("/signup")
	public User signUp(@RequestBody User user) {
		User userResult = userService.signUp(user);
		return userResult;
	}

}
@Service
public class UserService {

	@Autowired
	UserRepository userRepo;

	public User signUp(User user) {
		User userResult = userRepo.save(user);
		return userResult;
	}

}

userRepo에 save라는 메소드를 생성하지 않았지만 CrudRepository를 상속하였기 때문에 기본적인 CRUD의 메소드를 바로 사용할 수 있다. ㄹㅇ 편리 ㅎㅎ

CrudRepository가 기본적으로 제공하는 메소드는 다음과 같다.

Modifier and Type Method and Description
long count()

Returns the number of entities available.

void delete(T entity)

Deletes a given entity.

void deleteAll()

Deletes all entities managed by the repository.

void deleteAll(Iterable<? extends T> entities)

Deletes the given entities.

void deleteById(ID id)

Deletes the entity with the given id.

boolean existsById(ID id)

Returns whether an entity with the given id exists.

Iterable<T> findAll()

Returns all instances of the type.

Iterable<T> findAllById(Iterable<ID> ids)

Returns all instances of the type T with the given IDs.

Optional<T> findById(ID id)

Retrieves an entity by its id.

<S extends T>
S
save(S entity)

Saves a given entity.

<S extends T>
Iterable<S>
saveAll(Iterable<S> entities)

Saves all given entities.

그리고 createdAt 변수를 지난번처럼 정의하였더니 DB에 올라가질 않아서 엔티티가 되는 User 클래스는 다음과 같이 수정하였다. 참조

@Getter
@Setter
@ToString
@Entity
public class User {

	@Id
	private String email;
	private String name;
	private String birth;
	private String password;
	@Transient
	private String passwordCheck;
	private String gender;
	private LocalDateTime createdAt;

	@PrePersist
	public void createdAt() {
		this.createdAt = LocalDateTime.now();
	}

}

프로젝트를 실행하고 다음과 같이 포스트맨에서 RequestBody에 필요한 부분들을 입력해주었다.

정상 작동됨을 확인하였다.

 

2. REST API를 사용하여 회원가입 기능 구현

이메일 중복 체크와 비밀번호 확인을 할 수 있는 회원가입 기능을 구현하고자 한다.

@RestController
@RequestMapping("/user")
public class UserController {

	@Autowired
	private UserService userService;
	
	@PostMapping("/signup")
	public ResponseVO signUp(@RequestBody User user) throws Exception {
		
		int emailResult = userService.checkEmail(user);
		boolean pwResult = userService.checkPw(user);
		
		if (emailResult == 0 && pwResult == true) {
			User userResult = userService.signUp(user);
			ResponseVO result = new ResponseVO();
			result.setCode(HttpStatus.OK);
			result.setMessage("SUCCESS");
			result.setData(userResult);
			return result;
		} else {
			ResponseVO result = new ResponseVO();
			result.setCode(HttpStatus.INTERNAL_SERVER_ERROR);
			result.setMessage("FAIL");
			result.setData(null);
			return result;
		}
	}
    
}
@Service
public class UserService {

	@Autowired
	UserRepository userRepo;
	
	@Autowired
	TokenRepository tokenRepo;

	public int checkEmail(User user) {		
		String email = user.getEmail();
		int emailResult = userRepo.countByEmail(email);
		return emailResult;
	}
	
	public boolean checkPw(User user) {		
		logger.info("user: " + user);
		String password = user.getPassword();
		String passwordCheck = user.getPasswordCheck();
		
		if (password.equals(passwordCheck)) {
			return true;
		} else
			return false;
	}
	
	public User signUp(User user) {
		User userResult = userRepo.save(user);
		return userResult;
	}
    
}
@Repository
public interface UserRepository extends CrudRepository<User, String> {
	
	public int countByEmail(String email);
    
}

눈여겨 볼 메소드는

public int countByEmail(String email);

이다.

MyBatis를 사용했으면 매퍼 파일에

<select id="checkEmail" resultType="int">
	<![CDATA[
		SELECT COUNT(*) FROM user
		WHERE email = #{email};
	]]>
</select>

다음과 같이 적었어야 하지만 UserRepository에 count메소드만 선언해주면 위와 같은 쿼리를 실행한다.

보다 복잡한 쿼리는 어떻게 다루어야 하는지는 더 공부가 필요할 듯 하다.

목차

1. perspective 설정

2. 새 프로젝트 생성

3. application.properties 작성

4. 엔티티 클래스 매핑

 

* Git Repository에 업로드

기존에 MyBatis로 DB를 사용했던 것을 JPA로 전환하려고 한다.

작업한 코드는 여기에 업로드하였다.

 

1. perspective 설정

Spring 선택

 

2. 새 프로젝트 생성

Spring Starter Project 선택

정보 입력

5가지 의존성 추가

 

3. application.properties 작성

# DataSource Setting
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/discountak?characterEncoding=UTF-8&serverTimezone=UTC
spring.datasource.username=사용자이름
spring.datasource.password=비밀번호

# JPA Setting
spring.jpa.hibernate.ddl-auto=update
spring.jpa.generate-ddl=false
spring.jpa.show-sql=true
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.properties.hibernate.format_sql=true

# Logging Setting
logging.level.org.hibernate=info

참고로 본 프로젝트는 이 책을 참조하였다.

책은 H2를 사용했지만 나는 MySQL을 사용하니 DB 드라이버만 다르게 하고 거의 비슷하게 설정하였다.

여기서, 

spring.jpa.hibernate.ddl-auto=update

update로 설정해야 하는 이유는, 이미 저장해놓은 데이터들이 있는 스키마를 사용하기 때문에 create로 작성하면 테이블이 중복되는 오류가 발생한다.

스프링부트에서 JPA를 사용하기 위한 보다 자세한 설명은 여기에서 볼 수 있다.

@RunWith(SpringRunner.class)
@SpringBootTest
class DiscounbootApplicationTests {

	@Test
	void contextLoads() {
	}

}

src/test/java의 기본 테스트 클래스에 다음과 같이 작성한 후 실행하면 MySQL과 정상적으로 연결됨을 확인할 수 있다.

4. 엔티티 클래스 매핑

JPA는 DB의 테이블이 아닌 엔티티를 통해 데이터를 관리하기 때문에 테이블과 매핑할 엔티티 클래스를 작성해야 한다.

기존 MyBatis를 사용할 때 domain 패키지의 VO 클래스와 비슷한 개념이다.

@Getter
@Setter
@ToString
@Entity
public class User {

	@Id @GeneratedValue
	private String email;
	private String name;
	private String birth;
	private String password;
	@Transient
	private String passwordCheck;
	private String gender;
	@Temporal(value = TemporalType.TIMESTAMP)
	private Date createdAt;

}

@Entity는 User 클래스를 엔티티 처리한다.

@Id, @GenerateValue는 변수 email을 식별자로 처리한다.

@Transient는 특정 변수를 영속 필드에서 제외할 때 사용한다. 테이블에 존재하지 않는 변수이기 때문에 @Transient 처리하였다.

 

자바 애플리케이션으로 프로젝트를 실행하면 정상 작동한다.

 

더보기

에러

user 외의 다른 엔티티 클래스도 작성하고 JUnit 테스트를 실행했더니 갑자기 뜨는 

'no test found with test runner 'junit 5' 에러...

이럴 때에는 침착하게 이클립스를 종료하고 재실행하면 정상 작동된다.

이런 에러 나만 뜨나 ㅋ


지금까지의 작업을 MyBatis와 비교해보고자 한다. MyBatis로 작업을 했다면, 해야 하는 설정이 한두 가지가 아니다.

VO클래스는 공통적으로 작성하였지만, MyBatis 환경에서는 추가적인 작업이 더 필요하다.

먼저 VO의 알리아스를 설정해야 하며,

매퍼파일에 일일이 쿼리를 작성해주어야 하는 번거로움이 있다. 

반면, 프로젝트를 생성할 때 추가해주었던 JPA 의존성으로 인해 생성된 JPA 스타터는 JPA 연동에 필요한 라이브러리들과 복잡한 xml 설정을 자동으로 처리하는 효율성을 가지고 있다. 

 

다음 글에서는 DAO 단을 JPA환경에서 어떻게 작성하는지 알아보고자 한다.

 

* 번외: Git Repository 네이밍

작업한 것들을 Git에 하나씩 올리려다가 문득 레포지토리 네이밍에 대해 생각해보게 되었다.

1. 소문자 사용

2. _(밑줄)이 아닌 -(줄표) 사용

3. 구체적으로 명시하기

4. 일관되게 작성하기

참조한 사이트

전체 텍스트 검색은 긴 문자의 텍스트 데이터를 빠르게 검색하기 위한 MySQL의 부가적인 기능이다. 전체 텍스트 검색을 사용하면 기사의 내용에 포함된 여러 단어들에 인덱스가 설정되어서 검색 시에 인덱스를 사용하여 검색 속도가 월등히 빨라진다.

전체 텍스트 인덱스는 신문기사와 같이 텍스트로 이루어진 문자열 데이터의 내용을 가지고 생성한 인덱스를 말한다.

SELECT * FROM 신문기사_테이블 WHERE 신문기사내용 '교통 사고의 증가로 인해 오늘 ----';
SELECT * FROM 신문기사_테이블 WHERE 신문기사내용 LIKE '교통%';

인덱스가 정렬되어 있으므로, 해당되는 내용이 인덱스를 통해서 빠르게 검색함.

SELECT * FROM 신문기사_테이블 WHERE 신문기사내용 LIKE '%교통%';

 

전체 테이블 검색을 하게 됨. 만약 10년치 기사에서 검색한다면 과부하가 발생하고 응답시간도 긺.

이것을 전체 텍스트 검색이 해결한다. 전체 텍스트 검색은 첫 글자 뿐 아니라 중간의 단어나 문장으로도 인덱스를 생성해 주기 때문에 이와 같은 상황에서도 인덱스(정확히는 전체 텍스트 인덱스)를 사용할 수 있어 순식간에 검색 결과를 얻을 수 있다.

전체 텍스트 인덱스는 신문기사와 같이, 텍스트로 이루어진 문자열 데이터의 내용을 가지고 생성한 인덱스를 말한다. MySQL에서 생성한 일반적인 인덱스와는 몇 가지 차이점이 있다.

전체 텍스트 인덱스는 InnoDB MylSAM 테이블만 지원한다.

- 전체 텍스트 인덱스는 char, varchar, text의 열에만 생성이 가능하다.

- 인덱스 힌트의 사용이 일부 제한된다.

- 여러 개의 열에 FULLTEXT 인덱스를 지정할 수 있다.

 

1. 전체 텍스트 인덱스 생성

3가지 방법:

CREATE TABLE 테이블이름(

             …

             열이름 데이터형식,

             …,

             FULLTEXT 인덱스이름 (열이름)

);
CREATE TABLE 테이블이름(

             …

             열이름 데이터형식,

             …,

);

ALTER TABLE 테이블이름

                 ADD FULLTEXT (열이름);
CREATE TABLE 테이블이름(

             …

             열이름 데이터형식,

             …,

);

CREATE FULLTEXT INDEX 인덱스이름

                 ON 테이블이름 (열이름);

 

2. 전체 텍스트 인덱스 삭제

ALTER TABLE 테이블이름
	DROP INDEX FULLTEXT (열이름);

3. 중지 단어(stopwords)

실제로 검색할 때 무시할 만한 단어들은 아예 전체 텍스트 인덱스로 생성하지 않는 편이 좋다.

이번

선거는

아주

중요한

행사이므로

모두

참여

바랍니다

이번’, ‘아주’, ‘모두’, ‘등과 같은 단어는 검색할 이유가 없으므로 제외한다. 이것이 중지 단어이다.

MySQL 5.7INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD 테이블에 약 36개의 중지 단어를 가지고 있다.

 

4. 전체 텍스트 검색을 위한 쿼리

전체 텍스트 인덱스를 생성한 후에 이를 사용하기 위한 쿼리는 일반 SELECT문의 WHERE절에 MATCH() AGAINST() 사용하면 된다.

MATCH (col1, col2, ...) AGAINST (expr [search_modifier])

search_modifier:
  {
       IN NATURAL LANGUAGE MODE
     | IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION
     | IN BOOLEAN MODE
     | WITH QUERY EXPANSION
  }

- 자연어 검색

특별히 옵션을 지정하지 않거나 IN NATURAL LANGUAGE MODE를 붙이면 자연어 검색을 한다. 자연어 검색은 단어가 정확한 것을 검색해 준다.

newspaper이라는 테이블의 article이라는 열에 전체 텍스트 인덱스가 생성되어 있다고 가정한다.

영화라는 단어가 들어간 기사를 찾으려면 다음과 같이 사용한다.

SELECT * FROM newspaper 
	WHERE MATCH(article) AGAINST('영화');

영화는’, ‘영화가등 검색 불가.

영화또는 배우두 단어 중 하나가 포함된 기사 검색.

SELECT * FROM newspaper 
	WHERE MATCH(article) AGAINST('영화 배우');

- BOOLEAN MODE 검색

단어나 문장이 정확히 일치하지 않는 것도 검색하는 것을 의미한다.

+

검색 필수

SELECT * FROM newspaper

                 WHERE MATCH(article) AGAINST('영화 +액션' IN BOOLEAN MODE);

-

검색 제외

SELECT * FROM newspaper

                 WHERE MATCH(article) AGAINST('영화 -액션' IN BOOLEAN MODE);

~

검색 부정(-보다 부드러운 방식)

SELECT * FROM newspaper

                 WHERE MATCH(article) AGAINST('영화 ~액션' IN BOOLEAN MODE);

영화를 찾되 액션이 없는 열보다 액션이 있는 열이 아래 순위

*

부분 검색

SELECT * FROM newspaper

                 WHERE MATCH(article) AGAINST('영화*' IN BOOLEAN MODE);

영화를’, ‘영화가’, ‘영화는

“”안에 있는 구문과 정확히 동일한 철자의 구문

부분 검색

SELECT * FROM newspaper

                 WHERE MATCH(article) AGAINST("재밌는 영화" IN BOOLEAN MODE);

재밌는 영화”, “재밌는 영화가

재밌는 한국 영화”, “재밌는 할리우드 영화불가

5. 실습

MySQL은 기본적으로 3글자 이상만 전체 텍스트 인덱스로 생성한다. 이러한 설정을 2글자까지 전체 텍스트 인덱스가 생성되도록 시스템 변숫값을 변경한다.

아직 인덱스를 만들지 않은 상태.

실행계획(전체 테이블 검색)

전체 텍스트 인덱스 생성.

실행계획(전체 텍스트 인덱스 검색)

남자또는 여자가 들어간 행 검색.

남자’, ‘여자모두 들어간 행 검색.

남자가 들어간 행 중에 여자는 제외된 행 검색.

전체 텍스트 인덱스로 만들어진 단어들.

원래 있던 전체 텍스트 인덱스를 삭제하고 불필요한 중지 단어를 지정.

 

'MySQL' 카테고리의 다른 글

MySQL 잠금기능  (0) 2020.02.20
MySQL view(뷰)  (0) 2020.02.17
MySQL SELECT문  (1) 2020.02.10

+ Recent posts