티스토리 뷰

https://ekeprl.tistory.com/71

 

[Spring AI] (2) : Yahoo Finance 연동 - 주식 가격 조회 구현

https://ekeprl.tistory.com/69 [Spring AI] (1) : Google Gemini 연동 - 뉴스 요약 구현https://ekeprl.tistory.com/68 [Spring Security - JWT] (6) : 재발급 / 로그아웃 API + Postman 테스트https://ekeprl.tistory.com/67 [Spring Security - JWT] (5)

ekeprl.tistory.com

 

이전 포스팅에서 Yahoo Finance 연동으로 주식 가격을 조회하는 기능을 만들었고,

이번 포스팅은 사용자의 관심 종목을 등록, 목표가를 설정하는 Watchlist기능을 구현해보려 한다.

 

1. Table구성

CREATE TABLE watchlist (
id           VARCHAR(36)   NOT NULL PRIMARY KEY,
user_id      VARCHAR(36)   NOT NULL,
symbol       VARCHAR(20)   NOT NULL,
name         VARCHAR(100)  NOT NULL,
target_price DECIMAL(18,2) NULL,
created_at   DATETIME      NOT NULL DEFAULT current_timestamp(),
FOREIGN KEY (user_id) REFERENCES users(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
SHOW CREATE TABLE users;

 

기존 users 테이블의 user_id를 FK로 연결해 1:N 구조를 설계했다.

 

2. WatchlistModel / WatchlistRequestModel 

// 전체필드 > DB 매핑용
data class WatchlistModel(
    var id : String,
    var userId : String,
    var symbol : String,
    var name : String,
    var targetPrice : Double?,
    var createdAt : String,
    )
// 클라이언트 요청값 DTO
data class WatchlistRequestModel(
    var symbol : String,
    var name : String,
    var targetPrice : Double?,
)

 

각 용도별로  Model클래스를 작성했다.

 

3. WatchlistMapper / Watchlist Mapper.xml

@Mapper
interface WatchlistMapper {
    fun insert(watchlist: WatchlistModel)
    fun findByUserId(userId: String): List<WatchlistModel>
    fun deleteById(id: String)
    fun findAll(): List<WatchlistModel>
}
<mapper namespace="com.ekeprl.stockly.watchlist.mapper.WatchlistMapper">

    <insert id="insert"             parameterType="com.ekeprl.stockly.watchlist.model.WatchlistModel">
        INSERT INTO watchlist (id, user_id, symbol, name, target_price, created_at)
        VALUES (#{id}, #{userId}, #{symbol}, #{name}, #{targetPrice}, #{createdAt})
    </insert>

    <select id="findByUserId"       parameterType="String"
                                    resultType="com.ekeprl.stockly.watchlist.model.WatchlistModel">
        SELECT id, user_id as userId, symbol, name, target_price as targetPrice, created_at as createdAt
        FROM watchlist
        WHERE user_id = #{userId}
    </select>

    <delete id="deleteById"         parameterType="String">
        DELETE FROM watchlist WHERE id = #{id}
    </delete>


    <select id="findAll" resultType="com.ekeprl.stockly.watchlist.model.WatchlistModel">
        SELECT id, user_id as userId, symbol, name, target_price as targetPrice, created_at as createdAt
        FROM watchlist
    </select>


</mapper>

 

각각 Watchlist Insert / Select / Delete를 실행할 수 있도록 작성했다.

**findall() 메서드는 추후 scheduled에 사용하기 때문에 다음 포스팅에서 다루도록 하겠다.**

 

4. WatchlistService

@Service
class WatchlistService(
    private val watchlistMapper: WatchlistMapper
) {
    fun add(userId: String, request: WatchlistRequestModel) {
        val watchlist = WatchlistModel(
            id = UUID.randomUUID().toString(),
            userId = userId,
            symbol = request.symbol,
            name = request.name,
            targetPrice = request.targetPrice,
            createdAt = LocalDateTime.now().toString()
        )
        watchlistMapper.insert(watchlist)
    }

    fun getList(userId: String): List<WatchlistModel> {
        return watchlistMapper.findByUserId(userId)
    }

    fun delete(id: String) {
        watchlistMapper.deleteById(id)
    }
}

 

  • add() — Request DTO를 받아 id/createdAt을 채운 완전한 Model로 변환 후 저장한다.
  • getList() — userId 기준으로 본인 관심 종목만 조회한다.
  • delete() — id 기준으로 삭제한다.

5. WatchlistController

@RestController
@RequestMapping("/watchlist")
class WatchlistController(
    private val watchlistService: WatchlistService
) {
    @PostMapping
    fun add(@RequestBody request: WatchlistRequestModel): ApiResponse<Unit> {
        val userId = SecurityContextHolder.getContext().authentication?.name
        userId?.let { watchlistService.add(it, request) }
        return ApiResponse.success()
    }

    @GetMapping
    fun getList(): ApiResponse<List<WatchlistModel>> {
        val userId = SecurityContextHolder.getContext().authentication?.name
        return ApiResponse.success(userId?.let { watchlistService.getList(it) })
    }

    @DeleteMapping("/{id}")
    fun delete(@PathVariable id: String): ApiResponse<Unit> {
        watchlistService.delete(id)
        return ApiResponse.success()
    }
}

 

  • add() — 요청 body와 인증된 userId를 묶어 Service로 전달한다.
  • getList() — 인증된 userId로 본인 목록만 조회한다.
  • delete() — PathVariable로 받은 id로 삭제 요청한다.

6. Postman 테스트

(1) 종목 등록

헤더
종목 등록 테스트

 

 

(2) 목록 조회

헤더
등록한 목록 조회 테스트

 

이번 포스팅에선 사용자가 직접 관심목록을 등록하고, 해당 목록을 조회하는 기능을 구현했다.

 

다음 포스팅은 등록한 종목의 주가를 주기적으로 체크 후 텔레그램을 통한 알림 서비스 기능을 구현해보려고 한다.

 

감사합니다.

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/08   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
글 보관함