WAVED is a challenge-based platform designed to motivate and support developers preparing for employment.
Users can join various sessions, set personal goals, and build consistency through daily certification and community interaction.
- Personalized Challenges: Tailored challenge sessions designed for developers preparing for jobs.
- Motivational System: Users deposit participation fees as a commitment incentive and earn rewards upon completion.
- Flexible Verification Methods: Each challenge supports customized verification types (image, text, GitHub, link).
- Community Interaction: Participants can share progress and feedback with peers in the same challenge session.
https://waved-likelion.site/ (service discontinued)
- Development: Feb 26, 2024 β Mar 22, 2024
- Testing & Refactoring: Apr 1, 2024 β Apr 26, 2024
π Postman API Spec
src
βββ main
βββ java/com/senity/waved
β βββ base
β β βββ config, exception, jwt, redis, security
β βββ common
β βββ domain
β βββ admin, challenge, challengeGroup, event, liked, member,
β β myChallenge, notification, paymentRecord, quiz, review, verification
β βββ Each includes controller, service, repository, dto, entity
βββ resources
βββ application.yml
βββ application-secret.yml
- Integrated Spring Security with Google OAuth 2.0 for authentication.
- Used Redis for managing refresh tokens.
- Implemented blacklist handling to block logged-out tokens until expiry.
Users can verify progress via image, text, link, or GitHub commit.
- Image verification: Uploaded via multipart and stored in Azure Blob Storage.
- GitHub verification: Integrated with GitHub API to check daily commit activity.
Implemented transaction-level isolation to ensure data consistency during concurrent like events.
Even with multiple simultaneous likes, transaction isolation prevents dirty reads and maintains atomicity.
Originally, each challenge session had to be created manually.
Added scheduling and auto-generation logic to create new challenge sessions dynamically,
improving operational efficiency and data accuracy.
Integrated with PortOne API for the entire payment process:
payment verification, post-payment validation, cancellation, and refund handling.
Implemented real-time notifications using Spring SSE (Server-Sent Events).
Admins can immediately notify users about canceled verifications or status updates.
Defined a global exception handler for consistent error response structure across the API.
Each exception type returns an appropriate HTTP status code and message,
reducing redundancy and improving collaboration.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MemberNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ResponseEntity<ResponseDto> handleMemberNotFoundException(MemberNotFoundException e) {
return ResponseDto.of(HttpStatus.NOT_FOUND, e.getMessage());
}
@ExceptionHandler(WrongGithubInfoException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<ResponseDto> handleWrongGithubInfoException(WrongGithubInfoException e) {
return ResponseDto.of(HttpStatus.BAD_REQUEST, e.getMessage());
}
}Observed unnecessary data loading and tested multiple partial query methods for performance improvement.
a. Performance Tests
Test1 - Elapsed time: 112 ms # Default entity query
Test2 - Elapsed time: 673 ms # QueryDSL partial select
Test3 - Elapsed time: 601 ms # Projection partial select
Test4 - Elapsed time: 471 ms # DTO partial selectb. Query Analysis
Compared full entity vs DTO projections β
DTO-based partial select improved readability and maintenance but had higher overhead,
so the default entity query was retained for efficiency.
Hibernate:
select
m1_0.id,
m1_0.auth_level,
m1_0.birth_year,
m1_0.create_date,
m1_0.email,
m1_0.gender,
m1_0.github_id,
m1_0.github_token,
m1_0.has_info,
m1_0.has_new_event,
m1_0.job_title,
m1_0.modified_date,
m1_0.nickname
from
member m1_0
where
m1_0.id=?
Hibernate:
/* SELECT
new com.senity.waved.domain.challenge.service.NicknameDto(m.nickname)
FROM
Member m
WHERE
m.id =:id */ select
m1_0.nickname
from
member m1_0
where
m1_0.id=?
c. Memory Usage
Test1 - Elapsed time: 103 ms | Memory: 4,527,440 bytes
Test2 - Elapsed time: 564 ms | Memory: 23,068,640 bytes Result: ~5Γ higher memory usage in DTO-based queries β reverted to full entity select.
Due to session-based challenge structure, complex relationships caused redundant data loading.
a. Redefined JPA Mappings
Removed unnecessary relationships and optimized entity mappings.
Maintained referential integrity using explicit foreign key constraints.
b. EntityβDTO Conversion
Introduced DTO conversion to achieve separation of concerns,
improving flexibility and maintainability of service layers.
Result:
Prevented infinite loops during entity fetching.
Reduced query calls, improving load time from 3s β 1s on βMy Challengeβ page.