이제 예외처리를 해야한다. 예외처리가 필요한 이유는 courseId 를 통해 Course를 조회해야 하는데, courseId에 해당하는 Course 가 없는 상황같은 것을 방지하기 위해서이다.
예외처리는 exception package를 만들어서 해결하는게, 이 예외처리는 우리가만든 것 전체에 영향을 미쳐야 하기에 도메인의 바로 밑에 만든다.
그리고 그 밑에 ModelNotFoundException 데이터클래스를 만든다.
data class ModelNotFoundException() :
RuntimeException()
이렇게 하는데, 앞으로도 예외처리는 RuntimeException을 쓰면 된다.
data class ModelNotFoundException(val modelName: String, val id: Long?) :
RuntimeException("Model $modelName not found with given id: $id")
이런식으로 완성하면 된다. 그러면 이제 id에 해당하는 코스가 없으면 메시지가 나온다. 그럼 이제 코스서비스아이엠피엘 로 가서 관련 내용을 주석으로 추가해서 이런 내용이 있다는걸 알려준다.
override fun getCourseById(courseId: Long): CourseResponse {
// TODO: 만약 courseId에 해당하는 Course가 없다면 throw ModelNotFoundException
// TODO: DB에서 courseId에 해당하는 Course를 가져와서 CourseResponse로 변환 후 반환
TODO()
}
그리고 이런걸 courseId를 인식하는 녀석들 전부에 집어넣으면 된다.
@Service
class CourseServiceImpl: CourseService {
override fun getAllCourseList(): List<CourseResponse> {
// TODO: DB에서 모든 Course를 가져와서 CourseResponse로 변환 후 반환
TODO()
}
override fun getCourseById(courseId: Long): CourseResponse {
// TODO: 만약 courseId에 해당하는 Course가 없다면 throw ModelNotFoundException
// TODO: DB에서 courseId에 해당하는 Course를 가져와서 CourseResponse로 변환 후 반환
TODO()
}
override fun createCourse(request: CreateCourseRequest): CourseResponse {
// TODO: request를 Course로 변환 후 DB에 저장
TODO()
}
override fun updateCourse(courseId: Long, request: UpdateCourseRequest): CourseResponse {
// TODO: 만약 courseId에 해당하는 Course가 없다면 throw ModelNotFoundException
// TODO: DB에서 courseId에 해당하는 Course를 가져와서 request로 업데이트 후 DB에 저장, 결과를 CourseResponse로 변환 후 반환
TODO()
}
override fun deleteCourse(courseId: Long) {
// TODO: 만약 courseId에 해당하는 Course가 없다면 throw ModelNotFoundException
// TODO: DB에서 courseId에 해당하는 Course를 삭제, 연관된 CourseApplication과 Lecture도 모두 삭제
TODO()
}
}
createCourse 와updateCourse , deleteCourse 같은 애들은 예외상황이 발생했을때 일부만 작동되거나 하면 안되고 전부 멈춰야 하니까 위에 @Transactional을 추가해야 하는데, 이것도 추가하려면 build.gradle.kts 의 dependencies에 implementation("org.springframework.boot:spring-boot-starter-data-jpa") , implementation("com.h2database:h2") 을 추가해야함.
implementation("com.h2database:h2") 이게 없어도 오류는 안나는데, 실행이 제대로 안되니 필요.
참고로 dependencies에 추가하고 나선 옆에 코끼리 새로고침하는걸 잊으면 안된다. 어쨋든 그렇게 새로 고침을 하고, 그곳으로 돌아가서 알트 엔터를 하고 클래스 가져오기를 하면 된다.
그리고 이제 예외처리를 만들었으니 그것에 응답하는걸 만들어야 한다. 이것은 Web Layer에 속한다.
보통 에러가 뜨면 Whitelabel Error Page가 뜨는데, 그게 아니라 에러에 대한 답을 줘야 하기에 따로 만들어줘야한다.
그럼 먼저 코스컨트롤러로 가서, 딜리트 밑에
@ExceptionHandler(ModelNotFoundException::class)
fun handleModelNotFoundException(e: ModelNotFoundException): ResponseEntity<ErrorResponse> {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ErrorResponse(e.message))
}
이걸 해주고 코스 아이디로 가서 TODO에 에러 메시지를 출력하도록 지정후 스웨거로 돌아가서 확인해보면 해당 메시지가 뜬걸 확인할수 있지만, 문제는 하나하나 일일이 지정해야 한다는것. 그걸 해결하기 위해선,
먼저 exception package 밑에 dtp페키지를 만들고, 이안에 ErrorResponse 클래스를 만들어서 넣는다.
그리고 내용물은
data class ErrorResponse(val message: String?)
로 만든뒤에 exception페키지 밑에 GlobalExceptionHandler 클래스를 만든다. 참고로 이건 오류 메시지를 띄울수 있지만 메시지가 없을수도 있어서 ?를 넣는다.
@RestControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(ModelNotFoundException::class)
fun handleModelNotFoundException(e: ModelNotFoundException): ResponseEntity<ErrorResponse> {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ErrorResponse(e.message))
}
}
이걸로 전역적으로 404 낫파운드를 띄울수있다. 그리고 ResponseEntity<ErrorResponse>를 통해 위의 에러리스폰스와 연결되고 메시지를 출력한다.
그럼이제 스웨거로 이동해서, get course cousweId로 간 다음 코스아이디에 대충 1을 입력하면,
이렇게 에러가 잘 뜨는걸 확인할수있다.
그럼이제, 코스 왜에 다른 녀석들 한테도 service layer을 추가해야 하는데, 단순화를 위해 CoursService 에 관련 로직들을 구현하는 것을 목표로 한다.
interface CourseService {
fun getAllCourseList(): List<CourseResponse>
fun getCourseById(courseId: Long): CourseResponse
fun createCourse(request: CreateCourseRequest): CourseResponse
fun updateCourse(courseId: Long, request: UpdateCourseRequest): CourseResponse
fun deleteCourse(courseId: Long)
fun getLectureList(courseId: Long): List<LectureResponse>
fun getLecture(courseId: Long, lectureId: Long): LectureResponse
fun addLecture(courseId: Long, request: AddLectureRequest): LectureResponse
fun updateLecture(
courseId: Long,
lectureId: Long,
request: UpdateLectureRequest
): LectureResponse
fun removeLecture(courseId: Long, lectureId: Long)
fun applyCourse(courseId: Long, request: ApplyCourseRequest): CourseApplicationResponse
fun getCourseApplication(
courseId: Long,
applicationId: Long
): CourseApplicationResponse
fun getCourseApplicationList(courseId: Long): List<CourseApplicationResponse>
fun updateCourseApplicationStatus(
courseId: Long,
applicationId: Long,
request: UpdateApplicationStatusRequest
): CourseApplicationResponse
}
그 다음 코스서비스임플로 가면, 클래스 부분에 빨간줄이 그어져있는데, 알트 엔터 후 멤버 생성을 누르면, 자동적으로 이 코스서비스에 TODO가 생성된다.
@Service
class CourseServiceImpl: CourseService {
override fun getAllCourseList(): List<CourseResponse> {
// TODO: DB에서 모든 Course를 가져와서 CourseResponse로 변환 후 반환
TODO()
}
override fun getCourseById(courseId: Long): CourseResponse {
// TODO: 만약 courseId에 해당하는 Course가 없다면 throw ModelNotFoundException
// TODO: DB에서 courseId에 해당하는 Course를 가져와서 CourseResponse로 변환 후 반환
TODO()
}
@Transactional
override fun createCourse(request: CreateCourseRequest): CourseResponse {
// TODO: request를 Course로 변환 후 DB에 저장
TODO()
}
@Transactional
override fun updateCourse(courseId: Long, request: UpdateCourseRequest): CourseResponse {
// TODO: 만약 courseId에 해당하는 Course가 없다면 throw ModelNotFoundException
// TODO: DB에서 courseId에 해당하는 Course를 가져와서 request로 업데이트 후 DB에 저장, 결과를 CourseResponse로 변환 후 반환
TODO()
}
@Transactional
override fun deleteCourse(courseId: Long) {
// TODO: 만약 courseId에 해당하는 Course가 없다면 throw ModelNotFoundException
// TODO: DB에서 courseId에 해당하는 Course를 삭제, 연관된 CourseApplication과 Lecture도 모두 삭제
TODO()
}
override fun getLectureList(courseId: Long): List<LectureResponse> {
TODO("Not yet implemented")
}
override fun getLecture(courseId: Long, lectureId: Long): LectureResponse {
TODO("Not yet implemented")
}
override fun addLecture(courseId: Long, request: AddLectureRequest): LectureResponse {
TODO("Not yet implemented")
}
override fun updateLecture(courseId: Long, lectureId: Long, request: UpdateLectureRequest): LectureResponse {
TODO("Not yet implemented")
}
override fun removeLecture(courseId: Long, lectureId: Long) {
TODO("Not yet implemented")
}
override fun applyCourse(courseId: Long, request: ApplyCourseRequest): CourseApplicationResponse {
TODO("Not yet implemented")
}
override fun getCourseApplication(courseId: Long, applicationId: Long): CourseApplicationResponse {
TODO("Not yet implemented")
}
override fun getCourseApplicationList(courseId: Long): List<CourseApplicationResponse> {
TODO("Not yet implemented")
}
override fun updateCourseApplicationStatus(
courseId: Long,
applicationId: Long,
request: UpdateApplicationStatusRequest
): CourseApplicationResponse {
TODO("Not yet implemented")
}
}
@Service
class CourseServiceImpl: CourseService {
override fun getAllCourseList(): List<CourseResponse> {
// TODO: DB에서 모든 Course 목록을 조회하여 CourseResponse 목록으로 변환 후 반환
TODO("Not yet implemented")
}
override fun getCourseById(courseId: Long): CourseResponse {
// TODO: 만약 courseId에 해당하는 Course가 없다면 throw ModelNotFoundException
// TODO: DB에서 ID기반으로 Course 가져와서 CourseResponse로 변환 후 반환
// TODO("Not yet implemented")
throw ModelNotFoundException(modelName = "Course", id = 1L)
}
@Transactional
override fun createCourse(request: CreateCourseRequest): CourseResponse {
// TODO: request를 Course로 변환 후 DB에 저장
TODO("Not yet implemented")
}
@Transactional
override fun updateCourse(courseId: Long, request: UpdateCourseRequest): CourseResponse {
// TODO: 만약 courseId에 해당하는 Course가 없다면 throw ModelNotFoundException
// TODO: DB에서 courseId에 해당하는 Course를 가져와서 request기반으로 업데이트 후 DB에 저장, 결과를 CourseResponse로 변환 후 반환
TODO("Not yet implemented")
}
@Transactional
override fun deleteCourse(courseId: Long) {
// TODO: 만약 courseId에 해당하는 Course가 없다면 throw ModelNotFoundException
// TODO :DB에서 courseId에 해당하는 Course를 삭제, 연관된 CourseApplication과 Lecture도 모두 삭제
TODO("Not yet implemented")
}
override fun getLectureList(courseId: Long): List<LectureResponse> {
// TODO: 만약 courseId에 해당하는 Course가 없다면 throw ModelNotFoundException
// TODO: DB에서 courseId에 해당하는 Course목록을 가져오고, 하위 lecture들을 가져온 다음, LectureResopnse로 변환해서 반환
TODO("Not yet implemented")
}
override fun getLecture(courseId: Long, lectureId: Long): LectureResponse {
// TODO: 만약 courseId, lectureId에 해당하는 Lecture가 없다면 throw ModelNotFoundException
// TODO: DB에서 courseId, lectureId에 해당하는 Lecture를 가져와서 LectureResponse로 변환 후 반환
TODO("Not yet implemented")
}
@Transactional
override fun addLecture(courseId: Long, request: AddLectureRequest): LectureResponse {
// TODO: 만약 courseId에 해당하는 Course가 없다면 throw ModelNotFoundException
// TODO: DB에서 courseId에 해당하는 Course를 가져와서 Lecture를 추가 후 DB에 저장, 결과를을 LectureResponse로 변환 후 반환
TODO("Not yet implemented")
}
@Transactional
override fun updateLecture(courseId: Long, lectureId: Long, request: UpdateLectureRequest): LectureResponse {
// TODO: 만약 courseId, lectureId에 해당하는 Lecture가 없다면 throw ModelNotFoundException
/* TODO: DB에서 courseId, lectureId에 해당하는 Lecture를 가져와서
request로 업데이트 후 DB에 저장, 결과를을 LectureResponse로 변환 후 반환 */
TODO("Not yet implemented")
}
@Transactional
override fun removeLecture(courseId: Long, lectureId: Long) {
// TODO: 만약 courseId에 해당하는 Course가 없다면 throw ModelNotFoundException
// TODO: DB에서 courseId, lectureId에 해당하는 Lecture를 가져오고, 삭제
TODO("Not yet implemented")
}
@Transactional
override fun applyCourse(courseId: Long, request: ApplyCourseRequest): CourseApplicationResponse {
// TODO: 만약 courseId에 해당하는 Course가 없다면 throw ModelNotFoundException
// TODO: 만약 course가 이미 마감됐다면, throw IllegalStateException
// TODO: 이미 신청했다면, throw IllegalStateException
TODO("Not yet implemented")
}
override fun getCourseApplication(courseId: Long, applicationId: Long): CourseApplicationResponse {
// TODO: 만약 courseId, applicationId에 해당하는 CourseApplication이 없다면 throw ModelNotFoundException
// TODO: DB에서 courseId, applicationId에 해당하는 CourseApplication을 가져와서 CourseApplicationResponse로 변환 후 반환
TODO("Not yet implemented")
}
override fun getCourseApplicationList(courseId: Long): List<CourseApplicationResponse> {
// TODO: 만약 courseId에 해당하는 Course가 없다면 throw ModelNotFoundException
// TODO: DB에서 courseId에 해당하는 Course를 가져오고, 하위 courseApplication들을 CourseApplicationResponse로 변환 후 반환
TODO("Not yet implemented")
}
@Transactional
override fun updateCourseApplicationStatus(
courseId: Long,
applicationId: Long,
request: UpdateApplicationStatusRequest
): CourseApplicationResponse {
// TODO: 만약 courseId, applicationId에 해당하는 CourseApplication이 없다면 throw ModelNotFoundException
// TODO: 만약 status가 이미 변경된 상태면 throw IllegalStateException
// TODO: Course의 status가 CLOSED상태 일시 throw IllegalStateException
// TODO: 승인을 하는 케이스일 경우, course의 numApplicants와 maxApplicants가 동일하면, course의 상태를 CLOSED로 변경
// TODO: DB에서 courseApplication을 가져오고, status를 request로 업데이트 후 DB에 저장, 결과를 CourseApplicationResponse로 변환 후 반환
TODO("Not yet implemented")
}
}
이것은 주석으로 수행할 일들를 적은 버전이다.
다음으론 렉쳐 컨트롤러와 연결할 차례이다.
@RequestMapping("/courses/{courseId}/lectures")
@RestController
class LectureController(
private val courseService: CourseService
) {
@GetMapping
fun getLectureList(@PathVariable courseId: Long): ResponseEntity<List<LectureResponse>> {
return ResponseEntity
.status(HttpStatus.OK)
.body(courseService.getLectureList(courseId))
}
@GetMapping("/{lectureId}")
fun getLecture(@PathVariable courseId: Long, @PathVariable lectureId: Long): ResponseEntity<LectureResponse> {
return ResponseEntity
.status(HttpStatus.OK)
.body(courseService.getLecture(courseId, lectureId))
}
@PostMapping()
fun addLecture(
@PathVariable courseId: Long,
@RequestBody addLectureRequest: AddLectureRequest
): ResponseEntity<LectureResponse> {
return ResponseEntity
.status(HttpStatus.CREATED)
.body(courseService.addLecture(courseId, addLectureRequest))
}
@PutMapping("/{lectureId}")
fun updateLecture(
@PathVariable courseId: Long,
@PathVariable lectureId: Long,
@RequestBody updateLectureRequest: UpdateLectureRequest
): ResponseEntity<LectureResponse> {
return ResponseEntity
.status(HttpStatus.OK)
.body(courseService.updateLecture(courseId, lectureId, updateLectureRequest))
}
@DeleteMapping("/{lectureId}")
fun removeLecture(@PathVariable courseId: Long, @PathVariable lectureId: Long): ResponseEntity<Unit> {
courseService.removeLecture(courseId, lectureId)
return ResponseEntity
.status(HttpStatus.NO_CONTENT)
.build()
}
}
@RequestMapping("/courses/{courseId}/applications")
@RestController
class CourseApplicationController(
private val courseService: CourseService
) {
@PostMapping
fun applyCourse(
@PathVariable courseId: Long,
applyCourseRequest: ApplyCourseRequest
): ResponseEntity<CourseApplicationResponse> {
return ResponseEntity
.status(HttpStatus.CREATED)
.body(courseService.applyCourse(courseId, applyCourseRequest))
}
@GetMapping()
fun getApplicationList(@PathVariable courseId: Long): ResponseEntity<List<CourseApplicationResponse>> {
return ResponseEntity
.status(HttpStatus.OK)
.body(courseService.getCourseApplicationList(courseId))
}
@GetMapping("/{applicationId}")
fun getApplication(
@PathVariable courseId: Long,
@PathVariable applicationId: Long
): ResponseEntity<CourseApplicationResponse> {
return ResponseEntity
.status(HttpStatus.OK)
.body(courseService.getCourseApplication(courseId, applicationId))
}
@PatchMapping("/{applicationId}")
fun updateApplicationStatus(
@PathVariable courseId: Long,
@PathVariable applicationId: Long,
@RequestBody updateApplicationStatusRequest: UpdateApplicationStatusRequest
): ResponseEntity<CourseApplicationResponse> {
return ResponseEntity
.status(HttpStatus.OK)
.body(
courseService.updateCourseApplicationStatus(
courseId,
applicationId,
updateApplicationStatusRequest
)
)
}
}
CourseApplicationController을 이렇게 바꾼다. 그리고 유저의 서비스 밑에 UserService 인터페이스를 추가.
interface UserService {
fun signUp(signUpRequest: SignUpRequest): UserResponse
fun updateUserProfile(userId: Long, updateUserProfileRequest: UpdateUserProfileRequest): UserResponse
}
그리고 같은 위치에 유저서비스임플을 추가,
@Service
class UserServiceImpl : UserService {
@Transactional
override fun signUp(request: SignUpRequest): UserResponse {
// TODO: Email이 중복되는지 확인, 중복된다면 throw IllegalStateException
// TODO: request를 User로 변환 후 DB에 저장, 비밀번호는 저장시 암호화
TODO("Not yet implemented")
}
@Transactional
override fun updateUserProfile(userId: Long, request: UpdateUserProfileRequest): UserResponse {
// TODO: 만약 userId에 해당하는 User가 없다면 throw ModelNotFoundException
// TODO: DB에서 userId에 해당하는 User를 가져와서 updateUserProfileRequest로 업데이트 후 DB에 저장, 결과를 UserResponse로 변환 후 반환
TODO("Not yet implemented")
}
}
마지막으로 유저컨트롤러를
@RestController
class UserController(
private val userService: UserService
) {
@PostMapping("/signup")
fun signUp(@RequestBody signUpRequest: SignUpRequest): ResponseEntity<UserResponse> {
return ResponseEntity
.status(HttpStatus.OK)
.body(userService.signUp(signUpRequest))
}
@PutMapping("/users/{userId}/profile")
fun updateUserProfile(@PathVariable userId: Long,
@RequestBody updateUserProfileRequest: UpdateUserProfileRequest
): ResponseEntity<UserResponse> {
return ResponseEntity
.status(HttpStatus.OK)
.body(userService.updateUserProfile(userId, updateUserProfileRequest))
}
}
참고로 @Transactional은 add, update, delete처럼 변형시키는거에 붙여야한다. get은 아니다.
이제 데이터베이스를 써야하는데, Supabase를 사용할것이다.
- SQL은 문법은 총 4 가지로 분류할 수 있다.
- DDL(Data Definition Language) - 데이터 정의 언어
- 데이터베이스 및 테이블의 구조를 정의하는 역할이다. 명령어를 입력하는 순간 즉시 반영.
- CREATE : 데이터베이스 및 테이블 생성
- ALTER : 데이터베이스 및 테이블 구조 수정
- DROP : 데이터베이스 및 테이블 삭제
- RENAME : 데이터베이스 및 테이블 이름 변경
- TRUNCATE : 데이터베이스 및 테이블 초기화(데이터 초기화)
- DML(Data Manipulation Language) - 데이터 조작 언어(가장 많이 씀)
- 내부 데이터 관리의 역할. 즉시 반영(Auto Commit) 되지않고, ROLLBACK으로 되돌릴 수 있다. 우리가 배운 트랜잭션의 하위 작업으로 설정됩니다.
- SELECT : 데이터 조회
- INSERT : 데이터 추가
- UPDATE : 데이터 수정
- DELETE : 데이터 삭제
- DCL(Data Control Language) - 데이터 제어 언어
- 데이터를 보호하기 위한 보안. 데이터베이스에 접근하는 권한을 부여하거나 박탈할 수 있다.
- GRANT : 권한 설정
- REVOKE : 권한 삭제
- TCL(Transaction Control Language) - 트랜잭션 제어 언어(가장 쓸일 없음)
- 우리가 배운 트랜잭션을 제어하는 역할. @Transactional 어노테이션을 통한 여러 DML의 트랜잭션은 최종적으로 아래의 명령어로 변환되어 수행.
- COMMIT : 트랜잭션을 정상적으로 처리
- ROLLBACK : 트랜잭션을 다시 되돌림
- SAVEPOINT : COMMIT 이전의 특정시점을 지정할 수 있도록 하여 반영 혹은 ROLLBACK을 가능하게함
CREATE DATABASE 데이터베이스이름;
CREATE TABLE 테이블이름
(
컬럼이름1 컬럼타입1 제약조건,
컬럼이름2 컬럼타입2 제약조건,
...
[CONSTRAINT FK제약조건이름
FOREIGN KEY (컬럼이름)
REFERENCES 참조테이블이름(참조컬럼이름)]
);
- CREATE TABLE 관련 제약 조건.
- NOT NULL : 해당 컬럼은 항상 NULL이 아닌 값이 들어가야한다.
- UNIQUE : 해당 컬럼은 중복된 값이 있으면 안됨.
- PRIMARY KEY : NOT NULL 과 UNIQUE의 특성을 모두 가진다.
- FOREIGN KEY : 다른 테이블의 PRIMARY KEY 와 연결 되는 컬럼. 테이블 간의 관련성을 표기함으로써, 데이터의 무결성을 보장.
Primary Key(기본 키) 와 Foreign Key(외래키)
프라이멀 키는 아이디 같은것이다. 두 데이터의 이름이 같을때 프라이멀 키로 구분이 가능하다.
- Foreign Key
만약, Post와 Comment를 한 테이블에서 표현하려면 어떻게 될까? 아래처럼 Post 데이터가 중복된다.
post_id | post_title | comment_id | comment_content |
1 | 포스트1 | 1 | 첫 댓글 |
1 | 포스트1 | 2 | 다음 댓글 |
2 | 포스트2 | 3 | 댓글1 |
2 | 포스트2 | 4 | 댓글2 |
이를 방지하기 위해, 우리는 일반적으로 모델별로 테이블을 분리한다.
post_id | post_title |
1 | 포스트1 |
2 | 포스트2 |
comment_id | comment_content |
1 | 첫 댓글 |
2 | 다음 댓글 |
3 | 댓글1 |
4 | 댓글2 |
하지만, 이러면 Comment 가 어떤 Post에 달린 건지 알 수 없다. 이때 Foreign Key를 사용. Foreign Key 제약조건을 걸어, Comment가 참조하는 Post가 Post Table내에 존재하고, 유일한 값이라는 것을 보장하고, Comment 테이블과 Post 테이블간의 연관성을 표현합니다!
· id | title |
1 | 포스트1 |
2 | 포스트2 |
id | content | post_id |
1 | 첫 댓글 | 1 |
2 | 다음 댓글 | 1 |
3 | 댓글1 | 2 |
4 | 댓글2 | 2 |
sql 쓰는건 sql online 치면 견본이 나와있으니 쓰면 된다.
테이블 간 관계: 1:1 관계, 1:N 관계, N:M 관계
1:1: 이렇게 일대일로 대응. FK는 어디든 상관 없음
1:N 관계: Post와 Comment가 대표적입니다! 부모 - 자식 관계라고도 부름. 밑에 보이듯 하나의 id가 여러게의 post id와 연결. FK는 N에 해당하는 자식 테이블
N:M 관계: 여러개의 레코드가 다른 테이블의 여러 레코드와 관계를 맺는 형태.
N:M 관계를 띄고있는 두 Table이 직접적으로 연결되진 않습니다. 대신에, 각각의 테이블과 1: N 관계를 형성하고 있는 테이블을 통해서 N:M 관계를 나타낼 수 있다.
예를 들자면, 내가 여러 동아리에 가입했는데, 동아리에 나말고도 많은 회원이 있는 그런것.
insert 부문
INSERT INTO 테이블이름(컬럼1, 컬럼2, 컬럼3, ...) VALUES(데이터값1, 데이터값2, 데이터값3, ...);
INSERT INTO 테이블이름 VALUES(데이터값1, 데이터값2, 데이터값3, ...);
INSERT INTO comment (id, content, post_id) VALUES (5, '댓글3', 1), (6, '댓글4', 2);
select 부문
SELECT 컬럼이름1, 컬럼이름2, ... FROM 테이블이름 [WHERE 조건]; 이건 하나만 조회
SELECT * FROM 테이블 이름 [WHERE 조건]; *붙으면 전체조회
SELECT title FROM post
SELECT * FROM comment WHERE post_id=1;
UPDATE 구문
UPDATE 테이블이름 SET 컬럼이름1=데이터값1, 컬럼이름2=데이터값2, ... WHERE 조건; UPDATE post SET title='새로운 타이틀' WHERE id=1;
DELETE 구문
DELETE FROM 테이블이름 WHERE 컬럼이름=데이터값;
연산자 - WHERE 뒤에 연산자를 활용하여 다양한 조건을 설정할 수 있다.
= , != , < , > , <= , >= : 일반적인 프로그램에서 사용하는 비교연산자와 동일한 기능
AND , OR , NOT : Kotlin에서 사용하는 && , || , ! 과 유사
ORDER BY , ASC, DESC : 결과를 컬럼을 기준으로 오름차순이나 내림차순으로 정렬
ORDER BY는 ORDER BY id 하고 아이디 순 정렬, ASC는 오름차순 DESC는 내림차순
SELECT * FROM comment WHERE id > 1 ORDER BY id DESC; 하면 아이디를 내림차순
LIMIT : 출력 결과의 수를 제한. SELECT * FROM comment LIMIT 2;
IS NULL , IS NOT NULL : 값이 NULL이거나 NULL이 아닌 컬럼을 검색
IN : 컬럼 값이 여러 값중 하나인 조건을 나타낼때 사용.
SELECT * FROM comment WHERE id IN (1, 2, 3);
LIKE : 특정 패턴의 문자열을 검색합니다. 패턴에는 % 와 _ 가 사용되고, % 는 0개 혹은 여러개의 문자, _ 는 하나의 문자를 의미.
SELECT * FROM comment WHERE content LIKE '%댓글%';
BETWEEN : 컬럼 값의 범위를 지정하여 조회
SELECT * FROM comment WHERE id BETWEEN 2 AND 4;
- 문법 참고 사이트 - https://www.w3schools.com/sql/
- 연습 및 학습 사이트 - https://sqlbolt.com/lesson/
이것으로 연습을 하는게 좋다고 한다.