728x90

콘서트 서비스에서 발생할 수 있는 동시성 상황

  1. 콘서트 좌석 신청 시 여러 명이 동시에 하나의 좌석을 요청하는 경우
  2. 임시예약한 좌석을 결제 요청할 경우
  3. 포인트 충전/사용의 경우

Lock 판별 기준

Optimistic Lock( 낙관적 락 )

@Version를 사용한 낙관적 락을 사용한 테스트입니다.

public class PointJpo {
    @Id
    private String userId;
    @Version
    private int entityVersion;
    private int point;
}

낙관적 락의 장점으로는 가장 간단하게 적용할 수 있고, 실제 DB락을 사용하지 않아 DB부하가 심하지 않다는 장점이 존재한다 생각합니다.

 

낙관적 락은 Update할 때 해당 데이터를 조회하여 version이 동일하다면 pass 다르다면 fail처리를 하는 Flow를 가지고 있습니다.

 

이로써 Transaction이 종료될 때 Update query가 발행되며 검사하는 만큼 Transaction초기에 해당 데이터의 일관성을 판단하고 Exception을 발생시키는 로직보다는 늦게 검증한다는 단점이 존재합니다.

 

또한 낙관적 락은 동시성을 처리할 때 처음 한번 수정이 이뤄졌다면, 나머지 동시에 요청된 트래픽들은 버전이 다르다면 전부 실패처리 해버려 충돌이 심한 로직의 경우 데이터의 수정에 있어 정확한 데이터를 얻을 수 있을 것이라는 보장이 힘들다 생각합니다.

 

그리고, 낙관적 락은 버전이 다를 경우 실패 처리해버리기 때문에 Retry를 사용하는 경우가 많은데, 이는 과한 메모리 사용등으로 인해 성능 저하를 일으킬 수 있으므로 적절한 상황을 염두하고 사용해야 합니다.

 

하여 낙관적 락은 초기 요청을 제외한 나머지 요청들이 실패하고, 충돌이 심하지 않은 곳에 사용하기 적절하다 생각합니다. 


Pessimistic Lock( 비관적 락 )

@Lock을 활용한 비관적 락을 사용한 테스트입니다.

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("select p from PointJpo p where p.userId = :id")
Optional<PointJpo> findByIdForLock(String id);

 

Point를 Charge 할 때 비관적 락을 적용하여 Lock을 걸었고, 배타 락( X-Lock )을 적용하여 데이터의 일관성을 보장하였습니다.

요청한 트래픽들이 대기하다 하나씩 수행되며, DB락을 걸어 DeadLock 발생 위험이 존재합니다.

 

공유 락 ( S-Lock )을 적용하였을 경우 DeadLock의 발생 빈도가 높아 비관적 락을 적용하여 테스트할 때는 배타 락을 사용하여 테스트하였습니다. 

 

비관적 락은 들어온 요청들에 대해 DeadLock이 발생하지 않으면 모두 수행하는 만큼 충돌이 잦아도 이를 허용할 수 있다 생각합니다.

그리하여 초기 요청을 제외한 나머지 요청은 실패 처리하는 것이 아닌 나머지 요청들도 작업이 이뤄져야 할 경우 사용하는 것이 적절하다 생각합니다.


Distrubuted Lock ( 분산 락 )

Redis를 활용한 분산락입니다.

분산락은 Lettuce를 사용한 심플 락Redisson을 사용한 Pub/Sub방식으로 구현 및 테스트를 진행해 보았습니다.

 

첫 번째로 Lettuce를 사용한 심플 락입니다.

심플락의 경우 RedisTemplate를 사용하여 Redis에 Lock을 저장하고 해당 Lock을 받아 처리하고 UnLock을 하는 방법으로 진행하였습니다.

 

심플 락은 여러 개의 요청이 들어올 경우 동일한 Lock Key를 사용할 경우 하나의 요청만 Lock을 할당해 주고 나머지는 튕겨내는 방식으로 이루어지는 Lock입니다. 이후 Retry 로직을 통해 Spin Lock으로 변형하여 사용할 수 있습니다.

Point충전 테스트를 Simple Lock을 활용하여 구현한 결과입니다.

 

10개의 요청을 시도하였으며, 동일한 userId를 사용하여 하나의 요청을 제외하고 나머지는 실패함으로써 한 번만 요청이 실행된 것을 확인할 수 있습니다.

 

심플 락의 경우 락 획득 후 에러로 인해 락 점유 해제를 하지 않는 다면 락이 계속 점유되어있어 무한 로딩에 빠질 수 있다는 단점이 존재합니다.

 

이러한 점을 주의하셔서 락은 획득 후 n초 후 락 점유 해제가 되도록 처리 로직을 추가하는 것을 권장합니다. 

Lock을 획득한 트래픽은 수행하고 실패한 트래픽은 실패하는 상황

 

두 번째로 Redisson을 사용한 Pub/Sub 방식의 분산 락입니다. 

 

Redisson의 경우 Pub/Sub방식을 사용하고 있으며, 내부적으로 retry로직이 포함되어 있습니다.

Lettuce와 다르게 Lock을 시도하는 최대시간, Lock획득 후 점유하는 최대 시간을 손쉽게 설정할 수 있어  예외 상황으로 발생하는 Lock해제 실패로 인한 무한로딩 상황을 방지할 수 있으며, Retry기능으로 인해 동일한 키를 가진 여러 트래픽들을 수용할 수 있습니다.

 

단점이라고 하면, 지정한 시간 이외 처리될 트래픽들은 유실될 가능성이 크다는 단점을 가지고 있습니다.

위 테스트 결과만 보더라도 10건 중 5건을 처리하고 지정한 Lock점유 시간을 초과하여 트래픽이 유실된 상황입니다.

 

분산락의 경우 낙관적 락과 비관적 락에 비해 성능이 좋은 편은 아니라 생각하였습니다.

 

하지만, 지속적으로 트래픽을 점유하지 않아 DB부하를 Redis로 분산하여 관리한다는 점,

그리고 다양하게 Lock을 처리하면서 낙관/비관적 락에 비해 Custom과 락 핸들링이 좀 더 자유로운 점을 보아 다양한 상황에서 사용할 수 있다 생각합니다.

 

결론

위의 다양한 테스트들에 근거하여 다음 장에서 판단하는 상황별 어떤 락을 사용하는 것이 적합한가에 대해 판단을 하였습니다.


상황별 적용할 Lock

콘서트 좌석 신청 시 여러 명이 동시에 하나의 좌석을 요청하는 경우

적용 락: 낙관적 락
판단 근거:
콘서트 예약 즉 좌석 점유의 경우 한 명만 성공 처리하고 나머지는 실패처리를 하는 것이 정상적인 흐름이라고 판단하였습니다.

즉 비관적 락을 사용하여 동시성을 관리하는 것보다 Seat에 Version을 명시하여 Seat상태를 관리하는 방향으로 낙관적 락을 사용하는 것이 합당하다 생각하여 낙관적 락을 사용하겠습니다.

코드:

public class TemporaryReservationFlowFacade {
    @LoggingPoint  
    @Transactional  
    public String createTemporaryReservation(  
            String userId,  
            String concertId,  
            String seriesId,  
            String seatId  
    ) {  
        Concert concert = this.concertService.loadConcert(concertId);  
        // 콘서트 시리즈 조회  
        ConcertSeries concertSeries = this.concertSeriesService.loadConcertSeriesReservationAvailable(seriesId);  
        // 콘서트 좌석 조회  
        ConcertSeat concertSeat = this.concertSeatService.loadConcertSeatById(seatId);  
        // 좌석 예약  
        this.concertSeatService.reserveSeat(concertSeat.getSeatId());  
        // 임시 예약 생성  
        return this.temporaryReservationService.create(  
                userId,  
                concert.getConcertId(),  
                concert.getTitle(),  
                concertSeries.getSeriesId(),  
                concertSeat.getSeatId(),  
                concertSeat.getSeatRow(),  
                concertSeat.getSeatCol(),  
                concertSeat.getPrice()  
        );  
    }
}

테스트 결과

요청 유저 수: 100명
동시 접속 수: 100명 ( 동시 Thread 생성 수 10 ~ 15 )
원하는 결과: 1명만 좌석 임시예약에 성공하고 나머지는 실패하는 케이스


임시예약한 좌석을 결제 요청할 경우

적용 락: 낙관적 락
판단 근거:
임시 예약의 경우 결제를 하기 위해서는 본인이 신청한 좌석에 한해서 결제가 가능합니다.

즉 동시요청 상황의 경우 본인이 본인이 임시 예약한 정보를 결제요청하는 것이고, 이는 한번 성공하면 이후 요청에 있어 실패 처리를 하면 된다 생각합니다.

이때 TemporaryReservation에 paid라는 속성을 통해 결제 여부를 상태 관리하므로 낙관적 락을 통해 동시성을 처리하였습니다.


코드:

public class PaymentFlowFacade {
    @LoggingPoint  
    @Transactional  
    public String processTemporaryReservationPayment(  
            String temporaryReservationId,  
            String userId  
    ) {  
        TemporaryReservation temporaryReservation = this.temporaryReservationService.payReservation(temporaryReservationId);  
        int price = temporaryReservation.getPrice();  
        // 예약 테이블로 옮김  
        String reservationId = this.reservationService.create(  
                userId,  
                temporaryReservation.getConcertId(),  
                temporaryReservation.getTitle(),  
                temporaryReservation.getSeriesId(),  
                temporaryReservation.getSeatId(),  
                temporaryReservation.getSeatRow(),  
                temporaryReservation.getSeatCol(),  
                price  
        );
        // 결제 처리  
        String paymentId = this.paymentService.create(reservationId, userId, price);  
        //포인트 사용  
        this.pointService.use(userId, price);  
        this.pointHistoryService.createPointHistory(userId, price, PointHistoryStatus.USE, paymentId);  
        // 대기열 토큰 만료 처리  
        String waitingTokenId = this.waitingTokenService.deleteByUserIdAndSeriesId(userId, temporaryReservation.getSeriesId());  
        this.waitingQueueService.queuesExpiredByToken(waitingTokenId);  

        return paymentId;  
    }
}

테스트 결과

요청 유저 수: 1명
동시 접속 수: 10번 ( 한 유저가 비정상 프로그램을 사용하여 10번 동시 요청을 보낸 케이스 테스트 )
원하는 결과: 처음 임시예약을 결제하여 예약하고, 나머지는 취소 처리


포인트 충전/사용

적용 락: 낙관적 락
판단 근거:
낙관적 락의 단점이 될 수 있는 Retry요청의 경우 해당 상황에서는 실패 시 실패 처리 상황이므로 Retry로 인한 메모리 낭비 혹은 지연의 상황을 고려하지 않아도 됩니다.

또한 포인트 충전과 사용의 경우 동시성을 염두해야 하는 경우는 동시에 2번 이상 요청인 하나의 요청만 처리되어야 하는 케이스라 생각하여 낙관적 락으로 처리하였습니다.


코드:

public class PointFlowFacade {    
    @Transactional  
    public void chargePoint(String userId, int amount) {  
        //  
        this.pointService.charge(userId, amount);  
        this.pointHistoryService.createPointHistory(  
                userId,  
                amount,  
                PointHistoryStatus.CHARGE  
        );  
    }  

    @Transactional  
    public void usePoint(  
            String userId,  
            int amount,  
            String paymentId  
    ) {  
        //  
        this.pointService.use(userId, amount);  
        this.pointHistoryService.createPointHistory(  
                userId,  
                amount,  
                PointHistoryStatus.USE,  
                paymentId  
        );  
    }  
}

테스트 결과

요청 유저 수: 1명
동시 접속 수: 10번 ( 한 유저가 비정상 프로그램을 사용하여 10번 동시 요청을 보낸 케이스 테스트 )
원하는 결과: 1명만 좌석 임시예약에 성공하고 나머지는 실패하는 케이스

 

1. 10000포인트 충전 10번 시도 - 낙관적 락


2. 100000포인트 중 10포인트 사용 10번 시도 - 낙관적 락

 

Repository

 

GitHub - KrongDev/hhplus-concert: 콘서트 예약 서비스입니다.

콘서트 예약 서비스입니다. Contribute to KrongDev/hhplus-concert development by creating an account on GitHub.

github.com

 

728x90
728x90

서버 구축을 하는 5주 차가 마무리되었습니다.

 

요구사항 분석, 시퀀스 작성, ERD 작성, 서버 개발 등 다양한 작업이 있었는데요,

무엇보다 아픈 상황에서 진행을 하려다 보니 많이 힘들었던 것 같습니다.

 

사실 항해를 시작하면서 원했던 것이 다른 개발자분들은 어떻게 작성하고 있을까??

그리고 어떤 주제로 공부를 하고 이런 내용들을 제가 잘 이해하고 있을까?라는 점이 가장 궁금했는데요.

 

이러한 제가 원하던 부분들을 어느정도 경험을 할 수 있었고, 저 자신의 개발 실력에 대해 기본기부터 다져가며 학습을 하고 있는 것 같아 기분이 매우 좋은 상황입니다.

 

TDD를 제대로(?) 작성을 해보며 서버를 개발을 해보고 이 테스트 코드들이 CI에서 돌아가는 것들을 보면서 서버를 배포 시 문제 발생률은 테스트를 정확하게 했는지, 그리고 실패 케이스들은 예측을 하였고 그러한 실패 케이스들을 핸들링함으로써 해결을 하였는지를 검증하는 것이라 생각하였습니다.

 

무엇보다 실패 케이스가 가장 중요하다는 점인데요.

사실 저는 그냥 테스트 돌렸을 때 잘 저장되고 잘 작동하면 되는거 아니야??라는 생각이 많았습니다.

하지만 실패케이스를 강조를 하시는 것을 보고 왜 실패케이스가 중요할까 라고 생각을 해보니 실패 케이스를 예측하고 방지한다는 것은 결국 서버가 운영되었을 때 발생할 수 있는 오류 상황들을 인지하고 있고, 이를 방지하는 처리를 해놓았다는 말이 되는 것이라고 깨달았습니다.

 

만약 실패케이스를 제대로 잡지 않고 정상케이스만 테스트한다면 예상치 못한 상황들이 너무 많이 생겨버리고 그럼 오류가 터졌을 때 그 오류 케이스들을 잡아 내기에는 시간과 비용이 많이 들기 때문입니다.

 

덕분에 TDD의 중요성을 알게 되었습니다.

 

또한 TDD를 잘 작성하려면 모델 설계, 서비스간 의존성 등을 잘 설계하여 작성해야 하는데요.

이는 만약 여러 모듈들의 결합도나 호출 등이 너무 많고, 한 곳에서 모두 관리한다면 테스트의 시간도 오래 걸리고 하나의 메서드를 테스트하기 힘들어지기 때문입니다.

 

물론 하자면 못하는 게 없기는 하겠지만 그렇게 짠 코드가 가독성과, 확장성이 좋다고 생각이 들지는 않을 것 같습니다.

 

본인이 서버 개발을 한다면 혹은 어떤 개발을 하든지 잘 짠 코드 라고 했을 때 어떤 코드를 생각 하실지는 모르겠지만,

글쓴이는 잘짠 코드란 가독성과, 확장성이 좋으며, 적재적소에 존재하는 코드들을 작성하였을 때 잘 작성한 코드라고 생각합니다.

 

5주 차를 진행하면서 가장 좋았던 점은 테스트 코드라고는 알지 못하던 제가 테스트 코드를 짜게 되었고,

테스트 코드를 짜면서 실패 케이스들이란 어떤 것들이 있을지 고민하는 점을 보아 이렇게 서버 분석 및 설계를 좀 더 세세하게 살펴볼 수 있게끔 성장을 하였다는 점이 가장 마음에 드는 것 같습니다.

 

또한 개인적으로 제 강점은 관심사 분리를 잘한다는 점이라 생각하는데요.

이번 주차와 저번주차를 개발하면서 관심사 분리를 통한 코드 간결화등을 진행하며 테스트 코드 작성에 좋은 영향을 끼치는 것을 볼 수 있었습니다.

 

또한 관심사가 분리되다 보니 책임지는 로직, 테스트의 범위가 분리되어있어 보다 꼼꼼하게 테스트를 할 수 있었고, 어떤 점이 부족한지를 지속적인 리펙토링을 진행할 때에도 혼동이 없이 본인이 책임진 범위만을 수정하면 되니 굉장히 유지보수하기 편했던 점을 경험하였습니다.

 

 

728x90
728x90

JPA를 사용하여 데이터베이스에 락을 거는 방법은 2가지방법을 뽑아 볼 수 있습니다.

 

테이블에 행하는 행위 ( 조회, 수정, 등록, 삭제 )를 막는 비관적 락과 데이터에 버전을 명시하고 해당 버전을 통해 데이터의 일관성을 보장하는 낙관적 락이 이 경우입니다.

 

비관적 락  - Pessimistic Lock

비관적락은 데이터를 조회하고 특정 작업을 할 때 테이블에 어떤 작업도 일어나면 안되는 상황에서 사용하기 적합하다고 볼 수 있습니다.

이는 데이터의 정확성과 일관성을 보장하는 방법이며, 이 방법은 테이블에 락을 거는 행위로 성능에 큰 영향을 끼칠 수 있습니다.

 

데이터베이스의 종류에 따라 락을 걸었으나 데이터가 조회되는 경우도 있으니 이는 어떤 데이터베이스를 사용하는지를 확인을 하고 잘 선택하기 바랍니다.

public interface LectureJpaRepository extends JpaRepository<LectureEntity, Long> {
    //
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("select l from LectureEntity l where l.id = :id")
    Optional<LectureEntity> findByIdForUpdate(long id);
}

사용법은 위와 같이 조회할 때 @Lock 어노테이션을 통해 Lock을 시작하는 Start point를 설정해줍니다.

 

LockkModeType에는 아래와 같은 옵션들을 선택하여 적용할 수 있습니다.

LockModeType Action
READ Entity의 버전을 확인합니다. - jpa1.0 버전 호환
WRITE Entity가 변경이 되지 않더라도 버전을 자동으로 올려줍니다. - jpa1.0 버전 호환
OPTIMISTIC thread가 종료될 때 Entity의 버전을 확인합니다.
OPTIMISTIC_FORCE_INCREMENT Entity가 변경이 되지 않더라도 버전을 자동으로 올려줍니다.
PESSIMISTIC_READ 데이터베이스가 지원하면 비관적락을, 지원하지 않다면 명시적 락으로 통해 읽기는 가능하지만 CUD는 할 수 없어집니다.
PESSIMISTIC_WRITE 해당 쓰레드를 제외 나머지 모든 쓰레드는 lock이 해제될때까지 Block됩니다.
PESSIMISTIC_FORCE_INCREMENT 비관적으로 잠기고, Entity가 변경되지 않더라도 버전이 자동으로 오릅니다.
NONE 잠금을 걸지 않습니다.

 

public class LectureService {

    @Transactional
    public Lecture loadLecture(long lectureId) {
		// 조회 -  Lock시작
        // 작업들
        //종료 - Transaction이 종료될 때 Lock을 풉니다
    }

}

 

비관적 락의 경우 Transaction내에서 유지되며, 해당 영역 내에서 작업을 끝내야 데이터의 일관성을 유지할 수 있습니다.

너무 많은 처리 및 Transaction전파에 의해 Lock이 길어질 수 있어 주의가 필요합니다.

낙관적 락  - Optimistic Lock

Version데이터를 통해 데이터의 일관성을 보장하는 방법입니다.

 

비관적락과 달리 테이블을 잠그거나 하지는 않지만, 버전이 다를 경우 데이터 수정작업 등을 처리하지 않는 것으로 동시성을 처리하였습니다.

 

비관적락에 비해 사용하기 쉽다는 장점이 있으며, 테이블을 잠그지 않기 때문에 성능적인 측면에서 더욱 효율적으로 관리할 수 있는 방법입니다.

단점으로는 버전을 통해 데이터의 일관성을 유지하기 때문에 동시에 여러번의 요청이 들어올 경우 많은 데드락이 발생 할 수 있습니다.

 

아래와 같은 방법으로 Entity에  @Version어노테이션을 사용함으로써 간단하게 명시 할 수 있습니다.

@Entity
public class LectureEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    @Version
    private int version
}

 

버전의 타입으로는 int, Long, TimeStemp와 같은 자료를 사용할 수 있으며,  TimeStemp의 경우 데이터의 일관성을 보장하기에는 int와 Long타입에 비해 부적절하지만, 상황에 따라 사용하는 경우가 존재한다고 합니다.


데이터베이스에서 락을 거는 만큼 무분별한 락사용과 제대로 된 설계가 아닐 경우 큰 성능적인 이슈를 야기할 수 있습니다.

 

이를 염두하고 적절한 락을 사용하는 것을 권장합니다.

또한 이 블로그글은 Hibernate구현체, Mysql기준으로 작성된 만큼 다른 데이터베이스, 또한 다른 기능들과 같이 사용할 경우 위에 소개한 상황과 다른 상황들이 발생할 수 있으니 확인해보시기 바랍니다.

 

Hibernate공식문서: https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#locking

728x90

'Java' 카테고리의 다른 글

[ThreadLocal] Thread영역에 데이터를 저장하고 싶어요  (1) 2025.01.27
오차 없는 실수 연산 어떻게 해야 할까?  (0) 2025.01.10
[Java] Static  (3) 2024.12.26
[ Basic ] I/O Stream이란?  (0) 2024.12.23
JPA는 어떤 기술일까?  (0) 2024.04.12
728x90

1주차에는 TDD를 주 주제로 강의 및 과제를 하였습니다.

 

우선 TDD

TDD는 단위테스트, 통합테스트, E2E테스트로 이루어져있으며,

여기서 단위테스트는 하나의 Class내 작동 방식을 테스트한다고 보면 

통합테스트는 두개 이상의 모듈을 엮어 테스트 한다고 생각하면 좋을 것 같습니다.

 

E2E테스트는 실제 애플리케이션 구동 환경처럼 애플리케이션을 실행시켜하는 테스트로 API호출 부터 데이터베이스까지 조회, 및 테스트 시나리오대로 정상적으로 잘 작동하는지 테스트를 합니다.

 

단위테스트를 작성이 우선적으로 진행되고 그다음 테스트가 부족하면 통합테스트, 그다음 E2E테스트를 작성을 하는 것이라 생각하며,

TDD를 실제로 많이 사용하지 않았던만큼 테스트를 자동화 하는 부분을 고민해볼 것 같습니다.

 

 

 

 

GitHub - KrongDev/hhplus-tdd-java: 항해 플러스 1주차 TDD 동시성 제어 과제

항해 플러스 1주차 TDD 동시성 제어 과제. Contribute to KrongDev/hhplus-tdd-java development by creating an account on GitHub.

github.com

 

728x90
728x90

2024.06.15부터 항해99의 플러스 백엔드 5기가 시작하게 되었습니다.

 

개인적으로 플러스 백엔드를 시작하게 된 이유는 기본기의 부족함을 느껴서가 큰 이유인데요,

이번에 코칭 및 과제를 수행하면서 자신있는 부분과 부족한 부분을 찾을 수 있는 시간이 되었으면 좋겠습니다.

 

시작하는 마음은 기대반 두려움반인데요.

기대는 제대로 코칭 및 동료분들과 서로 협력하며 발전을 하는 것에 대한, 그리고 이제부터 수행하게될 과제들에 대한 기대가 많은 것 같습니다.

두려움은 앞으로 수행해야할 과제들에 대해 잘 수행할 수 있을 지, 문제는 발생하지 않을 지, 동료분들과 잘 지낼 수 있을까 마지막으로 제가 지금까지 학습한 내용들이 잘못 된 것은 아닐까 하는 마음이 큰거 같습니다.

 

적지 않은 금액을 지불하고 시작하는 만큼 적어도 제가 원하는 바는 이를 통해 이룰 수 있기를 바라는 마음이 커 열심히 10주간의 과정을 수행할 예정입니다. 

728x90
728x90

오늘은 코딩 테스트 사이트인 Hackerrank라는 사이트에 대해 소개를 해볼까 합니다.

 

Hackerrank는 최근 모 기업 코딩테스트를 보면서 처음 만나게 된 사이트인데,

코딩테스트, 기술 체크 등 포괄적으로 본인이 가지고 있는 기술을 체크할 수 있는 사이트인 거 같습니다.

 

코딩테스트의 경우 Hackerrank는 모든 문제가 영어로 작성되어있는 만큼 문제 해석에 시간이 오래 걸릴 수 있는데요,

이러한 점 때문에 프로그래머스, 백준 보다 문제 자체를 이해하는데 살짝 어렵다고 개인적으로 느꼈습니다.

 

코딩 문제의 경우 백준 기준 브론즈부터 골드 1문제까지의 난이도를 만나보았으며, 문제의 난이도와 종류 그리고 언어에 따른 문제들도 다양하게 존재하고 있습니다.

 

코딩 테스트를 연습하고 싶다!! 라고 한다면 30 Days of code 라는 챌린지 형태의 튜토리얼도 지원해 주어 코딩 테스트 준비가 어색하다 하면 한 번쯤 해보는 것을 추천드립니다. 난이도는 매우 쉬움부터 날이 갈수록 어려워지는 구조로 코딩 테스트라는 것에 익숙해지는데 도움을 많이 받았습니다.

 

일반 코딩테스트 문제들만 존재한다면 굳이 소개를 할 필요는 없겠죠 제가 소개하게된 큰 이유는 기술 검증 메뉴에 있습니다.

 

이 기술 검증이란 주어진 시간내에 해당 주제에 대해 여러 문제들을 주고, 이를 통과하였을 때 인증서를 제공하는 서비스인데요,

Java와 Sql등 여러 가지의 언어들과 테스트할 수 있는 것들이 존재하는 만큼 본인이 어느 정도 자신이 있다 생각하면 한번 응시하여 기술 검증을 받아보는 것이 좋을 것 같습니다.

 

기술 검증은 계정당 1회 가능하며 만약 통과를 하지 못할 경우 재응시가 불가 하니 주의 바랍니다.

추후 업데이트를 통해 재응시가 가능하도록 하겠다 하지만 언제 업데이트가 이뤄질지 모르니 공부하고 응시해 보시기를 권장합니다.

 

기술 검증에 통과하게 되면 아래와 같이 이쁜 인증서도 지급해주니 인증서를 모아보는 것도 하나의 취미로 삼아도 괜찮을 것 같습니다.

 

HackerRank Skill Certificate

Join over 23 million developers in solving code challenges on HackerRank, one of the best ways to prepare for programming interviews.

www.hackerrank.com

 

 

 

Dashboard | HackerRank

Join over 23 million developers in solving code challenges on HackerRank, one of the best ways to prepare for programming interviews.

www.hackerrank.com

 

728x90
728x90

작업 환경

OS: Ubuntu

Tool: Intellij

Builder: gradle

Framework: Spring-Boot

Language: Java

KafkaVersion: kafka_2.13

Kafka 설치 및 실행

  1. zookeeper 실행
# 다운받은 kafka 디렉토리 내에서 실행하였을 때 경로
xxx@kafka:~/kafka_2.13-3.6.1$ ./bin/zookeeper-server-start.sh ./config/zookeeper.properties 
  1. kafka-server 실행
xxx@kafka:~/kafka_2.13-3.6.1$ ./bin/kafka-server-start.sh ./config/server.properties
  1. topic 생성 - 이벤트 저장할 주제
xxx@kafka:~/kafka_2.13-3.6.1$ ./bin/kafka-topics.sh --create --topic ${원하는topic명:kafka-test} --bootstrap-server ${사용하길 원하는 주소:localhost:9092}
  1. topic 생성 확인
xxx@kafka:~/kafka_2.13-3.6.1$ ./bin/kafka-topics.sh --describe --topic ${topicName:kafka-test}  --bootstrap-server ${Address:localhost:9092}
Topic: kafka-test    TopicId: JW42CEjhS1qqnwIZ1cvdzQ    PartitionCount: 1    ReplicationFactor: 1    Configs: 
    Topic: kafka-test    Partition: 0    Leader: 0    Replicas: 0    Isr: 0
  1. Producer로 Event 발신
xxx@kafka:~/kafka_2.13-3.6.1$ ./bin/kafka-console-producer.sh --topic ${topicName:kafka-test} --bootstrap-server ${Address:localhost:9092}
>This is my first event
  1. Consumer로 Event수신
xxx@kafka:~/kafka_2.13-3.6.1$ ./bin/kafka-console-consumer.sh --topic ${topicName:kafka-test} --bootstrap-server ${Address:localhost:9092}

Docker로 작업

  1. Apache Kafka Image Download
docker pull apache/kafka:3.7.0
  1. Kafka Docker Container 생성 및 실행
docker run -p 9092:9092 apache/kafka:3.7.0 

 


참고

https://kafka.apache.org/quickstart

 

작업공간

https://github.com/KrongDev/kafka

728x90
728x90

초기화 단계

1. Awake

실행시 처음 한번만 실행

종속성 초기화를 할 때 사용

2. OnEnable

오브젝트 활성화 시 실행

초기화 혹은 오브젝트 재설정 할 때 사용

3. Start

첫번째 프레임이 업데이트 되기 전에 호출

Awake()이후 활성화 된 상태일 때 처음 한번만 호출

초기 설정할때 사용


게임 플레이 단계

1. FixedUpdate

물리연산( 고정 ) 주기로 실행

물리연산과 관련된 업데이트에 사용

2. Update

매 프레임마다 실행

게임 작동 로직등에 호출

3. LateUpdate

Update()호출 이후에 실행

애니메이션, 카메라 등 후처리에 사용


비활성화 및 종료 단계

7. OnDisable

오브젝트 비활성화시 실행

이벤트 핸들러 해제, 타이머 정지 등 필요한 정리 작업에 사용

8. OnDestroy

오브젝트 해체할 때 실행

메모리 해제 및 자원 정리 시 사용

728x90

'Unity' 카테고리의 다른 글

Basic Unity Moving  (0) 2024.06.02
Basic Unity Interface  (0) 2024.06.02

+ Recent posts