java spring Security 방문자 통계

강낭콩·2023년 7월 15일

방문자 통계

오늘 할 것은 누적, 투데이 수를 방문자 수를 받아 보려고 한다. react + spring으로 구성
스프링 시큐리티를 사용해서 로그인을 성공 했을때 누적을 할 것이다.

  • 원래 세션을 이용할때는 HttpSessionListener를 사용하지만 시큐리티를 이용하기 떄문에 시큐리티에서 성공시에 CustomHandler를 이용해서 이벤트를 발생시킬 것이다.
  • 로그인을 하면 오늘 방문을 했는지 파악을하고 없으면 1증가 있으면 카운트는 증가하지 않을 것 이다.

구현 기능

  1. 방문자수 증가
  2. 투데이 방문자수 가져오기
  3. 토탈 방문자수 가져오기
  4. 00시마다 투데이 방문자수 초기화 하기

투데이 방문자 수 구하기

그냥 저장소에서 email만 비교해서 가져오려고 했는데 그럼 다른날짜에 접속한 동일 인물이 있을 수 있다.
해결 -> 금일 날짜와 같이 비교를 해서 가져온다.

문제1. 총방문자수, 투데이방문자수 구하기

총 방문자수는 구할 수 있는데 투데이는 어떻게 계산할지 생각해봐야 한다.
일단 방문할때 날짜를 같이 적어 줬는데 지금 생각나는것은
1. SQL쿼리를 보낼때 날짜를 적어서 구하는 방법
2. 총 방문자를 다 구해오고 나서 자바코드에서 구별하기

해결 -> 총방문자수는 모든 칼럼을 카운트하고, 투데이는 금일 날짜와 비교후 가져온다.

문제2. 투데이 방문자 수 업데이트 기간

방문자수를 매일 00시에 업데이트를 할건데 이것은 어떤 방식으로 업데이트 할건지 생각해야 한다.

해결 -> 금일 날짜로 카운트를 하면 해결된다.

데이터 담을 객체 만들기

엔티티
투데이 계산하기위해 날짜를 같이 넣는다.

@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
@EntityListeners(AuditingEntityListener.class)//@CreatedDate를 설정했을떄 변경되는지 판단한다.
public class Visitor {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long vid;

    @CreatedDate
    private LocalDateTime regDate;

    private String email;

}

DTO



@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
@ToString
public class VisitorDTO {
    int TotalVisitorCount;
    int TodayVisitorCount;
    String email;
    LocalDateTime regDate;
}

레파지토리

JPQL을 사용했다.
1. 금일 로그인한 사람인지 파악하기위해 로그인시 email을 가져오고 금일 날짜와 AND로 비교해서 카운트를 증가시킴
2. 총 방문자수는 그냥 총 칼럼을 카운트해서 가져왔다.
3. 금일 방문자수는 오늘 날짜로 비교를 해서 카운트를 했다.

@Repository
public interface VisitorRepository extends JpaRepository<Visitor, Long> {

    @Query(value = "select v " +
            " from Visitor v" +
            " where v.email = :email AND substring(v.regDate, 0, 10)  like substring(:date, 0, 10)")
    Optional<Visitor> findByEmailAndRegDataLike(@Param("email") String email, @Param("date") LocalDateTime date);

    int countBy();

    @Query(value = " select count(*)" +
            " from Visitor v " +
            " where substring(v.regDate, 0, 10)  like substring(:date, 0, 10) ")
    int countByTodayCount(@Param("date") LocalDateTime date);
}

서비스

incrementVisitorCount만 보면 오늘 로그인 했는지 안했는지 파악 후 없으면 오늘 방문자에 추가함.

@Service
@RequiredArgsConstructor
public class VisitorServiceImpl implements VisitorService{
    private final VisitorRepository visitorRepository;

    @Override
    public void incrementVisitorCount(final String email) {
        //오늘 방문자중에 같은 이메일과 시간이 있는지 체크 후 없으면 추가하고 있으면 방문자 수를 늘리지 않는다.
        LocalDateTime date = LocalDateTime.now();
        if (!(CheckTodayVisit(email,date))) {
            VisitorDTO visitorDTO = VisitorDTO.builder()
                    .email(email)
                    .build();
            visitorRepository.save(DtoToEntity(visitorDTO));
        }
    }

    @Override
    public Boolean CheckTodayVisit(String email, LocalDateTime date) {
        Optional<Visitor> byEmailAndRegDataLike = visitorRepository.findByEmailAndRegDataLike(email, date);
        return byEmailAndRegDataLike.isPresent();//비어있으면 금일 접속 기록이 없으니 true
    }

    @Override
    public int getTotalVisitorCount() {
        return visitorRepository.countBy();
    }

    @Override
    public int getTodayVisitorCount() {
        LocalDateTime date = LocalDateTime.now();
        return visitorRepository.countByTodayCount(date);
    }
}

로그인 성공 핸들러

로그인을 성공했을때 실행되는것인데 시큐리티에서 원래 자동으로 해주지만 상속을 받아서 커스텀해서 사용할 수 있다. 로그인 했을때는 UserDetails에 데이터가 저장되어 있는데 이것을 authentication에서 꺼내쓸 수 있어서 아래와 같이 실행하면 된다.

//로그인 성공시 실행되는 핸들러
@Component
@RequiredArgsConstructor
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    private final VisitorService visitorService;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                        Authentication authentication) throws IOException, ServletException {
        // 방문자 수 증가 로직을 수행
        //로그인 성공시 UserDetails의 정보들을 가져온다.
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        String username = userDetails.getUsername();
        System.out.println("로그인성공핸들러 = " + username);
        visitorService.incrementVisitorCount(username);

        // 로그인 성공 후 처리할 로직을 추가할 수 있음

        // 기본적으로는 홈페이지 또는 로그인 후 이동할 페이지로 리다이렉트
        response.sendRedirect("/");
    }
}

0개의 댓글