Development/Spring Boot3 (Kotlin)

[Kotlin][SpringBoot3] Kopring 서버 기본 실습 11 - api의 기능 변경 실습

Tradgineer 2023. 9. 18. 08:00

 

1. 이전 포스팅

 

https://growingsaja.tistory.com/976

 

 

 

 

 

2. user 관련 api 수정 예정

 

 < 1차 >

 - login 시도시, 해당 fromIp가 존재하지 않으면 회원가입 처리 진행

 - login 시도시, 해당 fromIp가 존재하면 최근접속시간 업데이트하면서 로그인 승인

 - 로그인 시도 fromIp가 이전과 달라졌다면 계정을 비활성화시키는 대신, 정보 update를 하면 활성화시켜주면서 fromIp도 갱신

 

 

 

 

 

3. 수정사항 반영

 

// vim entities/User.kt

package com.example.practicekopring.entities

import org.springframework.data.annotation.Id
import org.springframework.data.mongodb.core.mapping.Document
import java.time.LocalDateTime

@Document(collection = "users")
data class User(
    @Id
    val id: String? = null,
    var userName: String? = null,
    var userNumber: String? = null,
    var isAvailable: Boolean? = true,
    var lastUpdatedAt_KST: String? = null,  // 데이터 최종 편집 일시 추가
    var lastUpdatedAt: LocalDateTime? = null,
    var createdAt_KST: String? = null,  // 데이터 초기 생성 일시 추가
    var createdAt: LocalDateTime? = null,
    var lastLoginAt_KST: String? = null,  // 최근 로그인 일시 추가
    var lastLoginAt: LocalDateTime? = null,
    var fromIp: String? = null, // 접속 ip : ip가 고유한 user name 역할을 합니다.
    val fromDevice_first: String? = null, // 첫 접속 디바이스
    var fromDevice_last: String? = null, // 최근 접속 디바이스
    val fromChannel_first: String? = null, // 첫 접속 채널
    var fromChannel_last: String? = null // 최근 접속 채널
)

 

// vim repositories/UserRepository.kt

package com.example.practicekopring.repositories

import com.example.practicekopring.entities.User
import org.springframework.data.mongodb.repository.MongoRepository

// UserRepository 인터페이스는 User 엔터티와 관련된 데이터베이스 작업을 위한 Repository입니다.
interface UserRepository : MongoRepository<User, String> {
    fun findByFromIp(fromIp: String): User?
    fun findByUserNumber(userNumber: String): User?
    fun findAllByOrderByFromIpAsc(): List<User>
    fun findByFromIpContaining(keyword: String): List<User>
}

 

// vim services/UserService.kt

package com.example.practicekopring.services

import com.example.practicekopring.entities.User
import com.example.practicekopring.entities.UpdateUserInfo
import com.example.practicekopring.repositories.UserRepository
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import java.time.LocalDateTime
import java.time.ZoneId

@Service
class UserService(private val userRepository: UserRepository) {

    // 1. 모든 사용자 조회
    fun getAllUsers(): List<User> {
        return userRepository.findAll()
    }

    // 2. 사용자 로그인 또는 생성
    fun loginUser(fromIp: String, userNumber: String, fromDevice: String, fromChannel: String): User? {
        // 현재 일시 가져오기
        val currentTime = LocalDateTime.now()
        val currenctTime_KST = currentTime.atZone(ZoneId.of("Asia/Seoul")).toString().substring(0, 19).replace("T", " ")  // 데이터 최종 편집 일시 설정
        val userByUserNumber = userRepository.findByUserNumber(userNumber)

        if (userByUserNumber == null) {
            // 로그인 실패시 회원가입 진행
            val user = User(fromIp = fromIp, fromDevice_first = fromDevice, fromChannel_first = fromChannel, fromDevice_last = fromDevice, fromChannel_last = fromChannel)
            user.userNumber = userNumber
            user.createdAt = currentTime
            user.createdAt_KST = currenctTime_KST
            user.lastLoginAt = currentTime
            user.lastLoginAt_KST = currenctTime_KST
            return userRepository.save(user)
        } else {
            // 로그인 성공 : userNumber가 있으면 무조건 성공은 시키지만, frontend에서 fromip 변경을 위해 update api를 이어서 콜해야함
            val id: String? = userByUserNumber.id
            val existingUser = id?.let { userRepository.findById(it) }
            if (existingUser != null) {
                if (existingUser.isPresent) {
                    val userToUpdate = existingUser.get()
                    // isAvailable 값을 토대로, 로그인시 정보 최신화 update 요구 화면을 띄워주면서 ip를 갱신할 수 있는 기회 제공 -> 만약 ip를 최신화하지 않으면 이후 기능들 사용 불가
                    if (userByUserNumber.fromIp != fromIp) {
                        // ip가 변경됨이 감지되면
                        userToUpdate.isAvailable = false
                    } else {
                        // ip 변경 없으면 다시 정상화
                        userToUpdate.isAvailable = true
                    }
                    // 작업 임시 저장
                    userToUpdate.lastLoginAt = currentTime
                    userToUpdate.lastLoginAt_KST = currenctTime_KST
                    userToUpdate.fromDevice_last = fromDevice
                    userToUpdate.fromChannel_last = fromChannel
                    return userRepository.save(userToUpdate)
                }
            } else {
                println("[ERROR] Login Fail. Not exist user.")
            }
            return null
        }
    }

    // 3. 사용자 업데이트
    fun updateUser(id: String, fromIp: String, userName: String, userNumber: String): User? {
        val existingUser = userRepository.findById(id)
        if (existingUser.isPresent) {
            val userToUpdate = existingUser.get()
            userToUpdate.fromIp = fromIp
            userToUpdate.userName = userName
            userToUpdate.userNumber = userNumber
            userToUpdate.isAvailable = true // 로그
            // 작업 일시 저장
            val currentTime = LocalDateTime.now()
            val currentTime_KST = currentTime.atZone(ZoneId.of("Asia/Seoul")).toString().substring(0, 19).replace("T", " ")  // 데이터 최종 편집 일시 설정
            // 한국 일시 저장
            userToUpdate.lastUpdatedAt = currentTime
            userToUpdate.lastUpdatedAt_KST = currentTime_KST
            return userRepository.save(userToUpdate)
        }
        return null
    }

    // 4. 사용자 업데이트 with body
    fun updateUserWithBody(user: UpdateUserInfo): User? {
        val existingUser = userRepository.findById(user.id ?: "")
        if (existingUser.isPresent) {
            val userToUpdate = existingUser.get()
            userToUpdate.fromIp = user.fromIp ?: userToUpdate.fromIp
            userToUpdate.userName = user.userName ?: userToUpdate.userName
            userToUpdate.userNumber = user.userNumber ?: userToUpdate.userNumber
            userToUpdate.isAvailable = true
            // 작업 일시 저장
            val currentTime = LocalDateTime.now()
            val currentTime_KST = currentTime.atZone(ZoneId.of("Asia/Seoul")).toString().substring(0, 19).replace("T", " ")  // 데이터 최종 편집 일시 설정
            // 한국 일시 string 저장
            userToUpdate.lastUpdatedAt = currentTime
            userToUpdate.lastUpdatedAt_KST = currentTime_KST
            return userRepository.save(userToUpdate)
        }
        return null
    }

    // 5. fromip로 검색
    fun searchUsersByFromIp(keyword: String): List<User> {
        return userRepository.findByFromIpContaining(keyword)
    }

    // 6. 사용자 삭제
    fun deleteUser(id: String) {
        userRepository.deleteById(id)
    }

    // 7. 사용자 카운트 조회
    fun getUserCount(): Long {
        return userRepository.count()
    }

    // 10. 사용자 정렬 조회 (예: 이름 오름차순)
    fun getUsersSortedByFromIp(): List<User> {
        return userRepository.findAllByOrderByFromIpAsc()
    }

    // 11. 특정 페이지의 사용자 조회 (페이징)
    fun getUsersByPage(page: Int, size: Int): Page<User> {
        val pageable: Pageable = PageRequest.of(page, size)
        return userRepository.findAll(pageable)
    }
}

 

// vim controllers/UserController.kt

package com.example.practicekopring.controllers

import com.example.practicekopring.entities.User
import com.example.practicekopring.entities.UpdateUserInfo
import com.example.practicekopring.services.UserService
import org.springframework.data.domain.Page
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.PathVariable


@RestController
@RequestMapping("/api/v1/users")
class UserController(private val userService: UserService) {

    // 1. 모든 사용자 조회
    @GetMapping("/")
    fun getAllUsers(): List<User> {
        return userService.getAllUsers()
    }

    // 2. 사용자 생성
    @PostMapping("/loginUser")
    fun loginUser(
        @RequestParam fromIp: String,
        @RequestParam userNumber: String,
        @RequestParam fromDevice: String,
        @RequestParam fromChannel: String
    ): User? {
        return userService.loginUser(fromIp, userNumber, fromDevice, fromChannel)
    }

    // 3. 사용자 업데이트
    @PutMapping("/update")
    fun updateUser(
        @RequestParam id: String,
        @RequestParam fromIp: String,
        @RequestParam userName: String,
        @RequestParam userNumber: String
    ): User? {
        val updatedUser = userService.updateUser(id, fromIp, userName, userNumber)
        return updatedUser
    }

    // 4. 사용자 업데이트 body 버전
    @PostMapping("/update-with-body")
    fun updateUserWithBody(
        @RequestBody user: UpdateUserInfo
    ): ResponseEntity<User>? {
        val updatedUser = userService.updateUserWithBody(user)
        return if (updatedUser != null) {
            ResponseEntity.ok(updatedUser)
        } else {
            ResponseEntity.notFound().build()
        }
    }

    // 5. 사용자 아이피 검색
    @PostMapping("/search-user-by-fromip")
    fun searchUsersByFromIp(
        @RequestParam keyword: String
    ): List<User> {
        return userService.searchUsersByFromIp(keyword)
    }


    // 6. 사용자 삭제
    @DeleteMapping("/{id}/delete")
    fun deleteUser(
        @PathVariable id: String
    ) {
        userService.deleteUser(id)
    }

    // 7. 사용자 카운트 조회
    @GetMapping("/count")
    fun getUserCount(): Long {
        return userService.getUserCount()
    }

    // 10. 사용자 정렬 조회 (예: 이름 오름차순)
    @GetMapping("/sorted-fromip")
    fun getUsersSortedByName(): List<User> {
        return userService.getUsersSortedByFromIp()
    }

    // 11. 특정 페이지의 사용자 조회 (페이징)
    @GetMapping("/paged")
    fun getUsersByPage(
        @RequestParam page: Int,
        @RequestParam size: Int
    ): Page<User> {
        return userService.getUsersByPage(page, size)
    }
}

 

 

 

 

 

4. 결과 정상 작동 확인하기