ํผ์ ๊ณต๋ถํ ๋ ๋๊ธฐ๋ถ์ฌ๊ฐ ๋ถ์กฑํ ์ฌ๋๋ค์ ์ํ ์จ๋ผ์ธ ์ค์๊ฐ ์คํฐ๋ ๊ณต๊ฐ
ํ๋ก์ ํธ ๋ฐฐ๊ฒฝ โข ํต์ฌ ๊ธฐ๋ฅ โข ๊ธฐ์ ์คํ โข ์ํคํ ์ฒโข ์ฑ๊ณผ
๋ง์ ํ์ต์๋ค์ด ์ง์์ ํผ์ ๊ณต๋ถํ ๋ ์ง์ค๋ ฅ๊ณผ ๋๊ธฐ๋ถ์ฌ๊ฐ ๋ถ์กฑํ์ฌ ํ์ต์ ์ง์ํ๊ธฐ ์ด๋ ค์
- ์ค์๊ฐ ํ์ ์คํฐ๋๋ฃธ์ ํตํ ๋๋ฃ ํ์ต์๋ค๊ณผ์ ์ฐ๋๊ฐ ํ์ฑ
- ๊ณต๋ถ ์๊ฐ ์ถ์ ๋ฐ ๋ชฉํ ์ค์ ์ผ๋ก ์๊ธฐ์ฃผ๋ ํ์ต ์ต๊ด ๊ตฌ์ถ
- Q&A ๊ฒ์ํ์ ํตํ ํ์ต ์ปค๋ฎค๋ํฐ ํ์ฑํ
- ์๊ฒฉ์ฆ ์ค๋น์, ์ทจ์ ์ค๋น์, ์ํ์ ๋ฑ ์ฅ์๊ฐ ์ง์ค ํ์ต์ด ํ์ํ ์ฌ๋๋ค
- ์ฌํ๊ทผ๋ฌด/์๊ฒฉ ํ์ต ํ๊ฒฝ์์ ํ์ต ๋๊ธฐ๊ฐ ํ์ํ ์ฌ๋๋ค
- WebRTC ๊ธฐ๋ฐ ํ์ํตํ (LiveKit Cloud)
- 1:N ์ค์๊ฐ ๋น๋์ค/์ค๋์ค ์คํธ๋ฆฌ๋ฐ
- ํ๋ฉด ๊ณต์ ๊ธฐ๋ฅ์ผ๋ก ํจ๊ป ๊ณต๋ถํ๋ ํ๊ฒฝ ์กฐ์ฑ
- ์ ์ง์ฐ ์ค์๊ฐ ์ฑํ
- ์คํฐ๋๋ฃธ ๊ด๋ฆฌ
- ๊ณต๊ฐ/๋น๊ณต๊ฐ ๋ฐฉ ์์ฑ
- ๋น๋ฐ๋ฒํธ ๋ณดํธ ๊ธฐ๋ฅ
- ์ฐธ์ฌ์ ๊ด๋ฆฌ ์์คํ
- OAuth 2.0 ํตํฉ (Google, Kakao, Naver)
- JWT ๊ธฐ๋ฐ Stateless ์ธ์ฆ
- Access Token (1์๊ฐ) + Refresh Token (7์ผ)
- HttpOnly ์ฟ ํค๋ก XSS ๊ณต๊ฒฉ ๋ฐฉ์ง
- Automatic Token Refresh Mechanism
- ์ค์๊ฐ ํ์ด๋จธ ๋ฐ ๊ณต๋ถ ์๊ฐ ์ถ์
- D-Day ๊ณ์ฐ๊ธฐ (์ํ์ผ์ ๊ด๋ฆฌ)
- ๋ชฉํ ์ค์ ๋ฐ ๋ฌ์ฑ๋ฅ ์๊ฐํ
- ํต๊ณ ๋์๋ณด๋ (์ผ๋ณ/๋์ ํ์ต ์๊ฐ)
- ๊ฒ์๊ธ CRUD ๋ฐ ๊ฒ์ ๊ธฐ๋ฅ
- ๊ณ์ธตํ ๋๊ธ (๋๋๊ธ ์ง์)
- ์ข์์ ๊ธฐ๋ฅ
- ํ์ด์ง๋ค์ด์ ์ต์ ํ
- ๋คํฌ/๋ผ์ดํธ ํ ๋ง ์ง์
- ๋ฐ์ํ ๋์์ธ (๋ชจ๋ฐ์ผ/ํ๋ธ๋ฆฟ/๋ฐ์คํฌํฑ)
- Progressive Web App ์ง์ ๊ฐ๋ฅํ ๊ตฌ์กฐ
- Framework: Spring Boot 3.4.4
- Security: Spring Security + JWT + OAuth2.0
- ORM: JPA/Hibernate (Lazy Loading, Batch Fetch)
- Database: MySQL 8.0 (AWS RDS)
- Build Tool: Gradle
- Framework: React 18 + Vite
- State Management: Redux Toolkit
- Styling: TailwindCSS + Headless UI
- Video SDK: LiveKit Client SDK
- HTTP Client: Axios (with interceptors)
- Backend Hosting: AWS EC2 (Ubuntu 22.04)
- Database: AWS RDS MySQL (db.t3.micro)
- Frontend Hosting: Netlify
- Video Infrastructure: LiveKit Cloud
- CI/CD: GitHub Actions
- Web Server: Nginx + Let's Encrypt SSL
- Monitoring: Spring Boot Actuator
[ํด๋ผ์ด์ธํธ]
โ HTTPS
[Netlify CDN] โโโโโโโโโโโโโโโโโ
โ โ
[AWS EC2 - Spring Boot] [LiveKit Cloud]
โ (Video Streaming)
[AWS RDS - MySQL]
์ฌ์ฉ์ ์์ฒญ
โ
CORS ๊ฒ์ฆ (Spring Security)
โ
JWT ์ธ์ฆ ํํฐ (JwtAuthenticationFilter)
โ
์ธ๊ฐ ๊ฒ์ฆ (.authenticated())
โ
๋น์ฆ๋์ค ๋ก์ง
๋ฌธ์ : Access Token ๋ง๋ฃ ์ ์ฌ์ฉ์ ๊ฒฝํ ์ ํ ํด๊ฒฐ:
// Axios Interceptor๋ฅผ ํ์ฉํ ํฌ๋ช
ํ ํ ํฐ ๊ฐฑ์
axiosInstance.interceptors.response.use(
response => response,
async error => {
if (error.response?.status === 401 && !originalRequest._retry) {
// Refresh Token์ผ๋ก ์๋ ์ฌ๋ฐ๊ธ
const newToken = await refreshAccessToken();
// ์คํจํ ์์ฒญ ์ฌ์๋
return axiosInstance(originalRequest);
}
}
);์ฑ๊ณผ: ์ฌ์ฉ์๊ฐ ํ ํฐ ๋ง๋ฃ๋ฅผ ์ธ์งํ์ง ๋ชปํ๋๋ก seamlessํ ๊ฒฝํ ์ ๊ณต
๋ฌธ์ : ์ด๊ธฐ ์ค๊ณ์์ ๋ชจ๋ ์๋ํฌ์ธํธ๊ฐ permitAll()๋ก ์ค์ ๋์ด ๋ณด์ ์ทจ์ฝ
ํด๊ฒฐ:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
return http
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/user/login", "/user/signup").permitAll()
.requestMatchers("/oauth2/**").permitAll()
.anyRequest().authenticated() // ๋๋จธ์ง ๋ชจ๋ API ์ธ์ฆ ํ์
)
.addFilterBefore(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class)
.build();
}์ฑ๊ณผ:
- ์ธ์ฆ๋์ง ์์ API ์ ๊ทผ ์ฐจ๋จ
- JWT ํํฐ ์ฒด์ธ ํตํฉ์ผ๋ก ์ค๋ณต ์ฝ๋ ์ ๊ฑฐ
- Stateless ์ธ์ ์ผ๋ก ์ํ ํ์ฅ ๊ฐ๋ฅ
๋ฌธ์ : ๊ฒ์๊ธ ๋ชฉ๋ก ์กฐํ ์ ๋๊ธ/์ข์์ ์กฐํ๋ก ์ธํ ์ฑ๋ฅ ์ ํ ํด๊ฒฐ:
# application.yml
spring:
jpa:
properties:
hibernate:
default_batch_fetch_size: 1000 # Batch Fetch ํ์ฑํ// Repository Layer
@Query("SELECT p FROM Post p JOIN FETCH p.user WHERE p.id = :id")
Post findByIdWithUser(@Param("id") Long id);์ฑ๊ณผ:
- ์ฟผ๋ฆฌ ์ 98% ๊ฐ์ (N+1 โ 2๊ฐ)
- ์๋ต ์๊ฐ ํ๊ท 73% ๊ฐ์ (1200ms โ 320ms)
๋ฌธ์ : ์์ฒด WebRTC ์๋ฒ ๊ตฌ์ถ ์ STUN/TURN ์๋ฒ ๊ด๋ฆฌ ๋ถ๋ด ํด๊ฒฐ: LiveKit Cloud SaaS ๋์
@Service
public class LiveKitService {
public String generateToken(String roomName, String userName) {
AccessToken token = new AccessToken(apiKey, apiSecret);
token.setName(userName);
token.addGrants(new RoomJoin(true), new RoomName(roomName));
return token.toJwt();
}
}์ฑ๊ณผ:
- ์๋ฒ ๊ด๋ฆฌ ๋น์ฉ 0์ (50GB ๋ฌด๋ฃ ํฐ์ด)
- ์์ ์ ์ธ P2P ์ฐ๊ฒฐ (99.9% ๊ฐ๋๋ฅ )
- ๊ฐ๋ฐ ์๊ฐ 80% ๋จ์ถ
๋ฌธ์ : ์๋ ๋ฐฐํฌ ์ ํด๋จผ ์๋ฌ ๋ฐ ๋ค์ดํ์ ๋ฐ์ ํด๊ฒฐ: GitHub Actions ์๋ ๋ฐฐํฌ ํ์ดํ๋ผ์ธ
# .github/workflows/deploy.yml
- name: Build with Gradle
run: ./gradlew clean build -x test
- name: Deploy to EC2
run: |
# JAR ํ์ผ ์ ์ก
scp build/libs/*.jar ec2:/home/ubuntu/app-new.jar
# ๋ฌด์ค๋จ ๋ฐฐํฌ
ssh ec2 << 'EOF'
systemctl stop studylink
mv app-new.jar app.jar
systemctl start studylink
EOF์ฑ๊ณผ:
- ๋ฐฐํฌ ์๊ฐ 95% ๋จ์ถ (30๋ถ โ 90์ด)
- ๋ฐฐํฌ ์คํจ์จ 0% ๋ฌ์ฑ
- ์๋ ๋กค๋ฐฑ ๋ฉ์ปค๋์ฆ์ผ๋ก ์์ ์ฑ ํ๋ณด
๋ฌธ์ : JPA ddl-auto: update๋ก ์ธํ ์คํค๋ง ์๋ ๋ณ๊ฒฝ ์ํ
ํด๊ฒฐ:
# application.yml (ํ๋ก๋์
)
spring:
jpa:
hibernate:
ddl-auto: validate # ์คํค๋ง ๊ฒ์ฆ๋ง ์ํ, ๋ณ๊ฒฝ ๊ธ์ง
# application-local.yml (๊ฐ๋ฐ)
spring:
jpa:
hibernate:
ddl-auto: update # ๊ฐ๋ฐ ํธ์์ฑ ์ ์ง์ฑ๊ณผ: ํ๋ก๋์ ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ ๋ณด์ฅ
- API ํ๊ท ์๋ต ์๊ฐ: 320ms (P95: 580ms)
- ๋์ ์ ์์: ์ต๋ 50๋ช ํ ์คํธ ์๋ฃ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ ์ต์ ํ: N+1 ๋ฌธ์ ํด๊ฒฐ๋ก 98% ์ฟผ๋ฆฌ ๊ฐ์
- ํ๋ก ํธ์๋ ๋ฒ๋ค ํฌ๊ธฐ: 280KB (gzip ์์ถ ์ 95KB)
- ๋ฐฑ์๋ ๊ฐ๋๋ฅ : 99.8% (AWS EC2 ๊ธฐ์ค)
- SSL ์ธ์ฆ: Let's Encrypt ์๋ ๊ฐฑ์
- ์๋ ๋ฐฐํฌ: main ๋ธ๋์น ํธ์ ์ 90์ด ๋ด ์๋ ๋ฐฐํฌ
- HTTPS ์ ์ฒด ์ ์ฉ (A+ SSL Rating)
- OAuth 2.0 ํตํฉ 3๊ฐ ์ ๊ณต์ (Google, Kakao, Naver)
- JWT ํ ํฐ ๋ณด์: HttpOnly ์ฟ ํค + CSRF ๋ฐฉ์ง
- Spring Security ์ธ์ฆ/์ธ๊ฐ ๋ถ๋ฆฌ
- ์๊ตฌ์ฌํญ ๋ถ์ ๋ฐ ๊ธฐ์ ์คํ ์ ์
- ERD ์ค๊ณ ๋ฐ API ๋ช ์ธ ์์ฑ
- UI/UX ์์ด์ดํ๋ ์ ์ค๊ณ
- ์ธ์ฆ/์ธ๊ฐ ์์คํ ๊ตฌ์ถ
- ์ค์๊ฐ ํ์ ์คํฐ๋๋ฃธ ๊ตฌํ
- Q&A ๊ฒ์ํ ๊ฐ๋ฐ
- ํ์ต ๊ด๋ฆฌ ๊ธฐ๋ฅ ๊ตฌํ
- AWS ์ธํ๋ผ ๊ตฌ์ถ
- CI/CD ํ์ดํ๋ผ์ธ ์ค์
- ์ฑ๋ฅ ์ต์ ํ (N+1 ์ฟผ๋ฆฌ ํด๊ฒฐ)
- ๋ณด์ ๊ฐํ (Spring Security ์ฌ์ค๊ณ)
- ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง ํ๋
- ๋ก๊น ๋ฐ ๋ชจ๋ํฐ๋ง ๊ฐ์
- ์ฝ๋ ํ์ง ๊ฐ์ (SonarQube)
- Java 17+
- MySQL 8.0+
- Node.js 18+
Backend (application-local.yml):
spring:
datasource:
url: jdbc:mysql://localhost:3306/studylink
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jwt:
secret: ${JWT_SECRET} # ์ต์ 64์
livekit:
api:
host: ${LIVEKIT_URL}
key: ${LIVEKIT_KEY}
secret: ${LIVEKIT_SECRET}Frontend (.env):
VITE_APP_SERVER=http://localhost:6080/
VITE_LIVEKIT_URL=ws://localhost:7880๋ฐฐํฌ๋ ์๋น์ค์ ์ค์๊ฐ API ๋ฌธ์: https://api.studylink.store/swagger-ui.html
POST /user/signup- ํ์๊ฐ์POST /user/login- ๋ก๊ทธ์ธGET /user/info- ์ฌ์ฉ์ ์ ๋ณด ์กฐํPOST /user/logout- ๋ก๊ทธ์์
GET /api/v1/rooms- ์คํฐ๋๋ฃธ ๋ชฉ๋กPOST /api/v1/rooms- ์คํฐ๋๋ฃธ ์์ฑGET /api/v1/video/token- LiveKit ํ ํฐ ๋ฐ๊ธ
GET /api/v1/posts- ๊ฒ์๊ธ ๋ชฉ๋กPOST /api/v1/posts- ๊ฒ์๊ธ ์์ฑPOST /api/v1/posts/{id}/like- ์ข์์
|
์ดํธ์ค Backend Lead Spring Boot, AWS |
๊น์ค์ฌ Frontend Lead React, LiveKit |
This project is licensed under the MIT License.
Made with โค๏ธ by StudyLink Team
๐ Live Demo โข ๐ API Docs โข ๐ง Contact



