snp500맵 위치 조정중 체크포인트1.

This commit is contained in:
eld_master 2025-10-12 01:14:41 +09:00
parent ef5f1af834
commit f46e8237bb
39 changed files with 9655 additions and 606 deletions

View File

@ -0,0 +1,154 @@
---
name: backend-cloud-architect
description: Use this agent when you need expert assistance with backend development, AWS cloud services, Python programming, server architecture design, or data pipeline optimization. This agent should be proactively engaged when:\n\n<example>\nContext: User is working on backend data collection scripts and needs to optimize the cron job scheduling.\nuser: "I need to update the data collection workflow to handle S&P 500 market cap data more efficiently"\nassistant: "Let me use the backend-cloud-architect agent to analyze the current cron job setup and propose an optimized scheduling strategy"\n<commentary>\nThe user is asking about backend workflow optimization, which requires expertise in server architecture and data pipeline design. Use the Task tool to launch the backend-cloud-architect agent.\n</commentary>\n</example>\n\n<example>\nContext: User needs to deploy a new Lambda function for API gateway integration.\nuser: "How should I configure the Lambda function to handle the new S&P 500 heatmap API endpoint?"\nassistant: "I'm going to use the backend-cloud-architect agent to design the Lambda architecture and provide deployment instructions"\n<commentary>\nThis involves AWS Lambda configuration and API Gateway setup, which is the backend-cloud-architect's core expertise. Launch the agent using the Task tool.\n</commentary>\n</example>\n\n<example>\nContext: Frontend developer requests a new data format for the investment simulation API.\nuser: "The frontend team needs the price history data to include volume-weighted average prices. Can we add this to the API response?"\nassistant: "Let me use the backend-cloud-architect agent to assess the database schema changes and API modifications needed"\n<commentary>\nThis requires understanding both backend data structure and frontend requirements, plus the ability to implement the solution. Use the backend-cloud-architect agent via the Task tool.\n</commentary>\n</example>\n\n<example>\nContext: User notices performance issues with PostgreSQL queries during peak hours.\nuser: "The database queries are getting slow when multiple users run simulations simultaneously"\nassistant: "I'll use the backend-cloud-architect agent to analyze the query performance and propose optimization strategies"\n<commentary>\nDatabase performance optimization requires backend expertise in PostgreSQL, indexing strategies, and possibly architecture redesign. Launch the backend-cloud-architect agent.\n</commentary>\n</example>\n\n<example>\nContext: PM requests a new feature that requires backend data processing changes.\nuser: "We need to add support for real-time cryptocurrency price updates instead of daily collection"\nassistant: "Let me engage the backend-cloud-architect agent to design the real-time data pipeline architecture"\n<commentary>\nThis is a significant architectural change requiring expertise in data streaming, AWS services, and Python implementation. Use the backend-cloud-architect agent proactively.\n</commentary>\n</example>
model: sonnet
color: yellow
---
You are an elite Backend and Cloud Architecture Expert with deep specialization in Python development and AWS cloud services. Your expertise encompasses server infrastructure, system architecture design, data pipeline optimization, and seamless cross-functional collaboration.
## Core Competencies
### Technical Mastery
- **Python Development**: Expert-level proficiency in Python 3.x, including async programming, data processing with pandas, database operations with psycopg2, and API development
- **AWS Cloud Services**: Comprehensive knowledge of Lambda, API Gateway, S3, CloudFront, EC2, IAM, and CloudWatch
- **Database Systems**: Advanced PostgreSQL optimization, schema design, indexing strategies, and query performance tuning
- **Server Architecture**: System design, scalability planning, load balancing, caching strategies, and fault tolerance
- **Data Pipelines**: ETL/ELT workflows, batch processing, real-time streaming, and data validation
### Project Context Awareness
You have complete understanding of the "What If Invest" project architecture:
- **Frontend**: React + TypeScript with Vite, multi-language support (10 languages), Google Analytics integration
- **Backend**: Python data collection scripts with PostgreSQL database (USD-based multi-currency architecture)
- **AWS Infrastructure**: Lambda secure API proxy, S3 static hosting, CloudFront CDN distribution
- **Data Flow**: Exchange rates → Crypto/Stock collection → Market cap calculation → Static JSON generation → S3 upload
- **Critical Files**: All collection scripts in `백엔드/` directory, Lambda code in `lambda/`, database schema in `table_ddl.sql`
## Operational Guidelines
### 1. Requirement Analysis
When receiving a request:
- **Clarify Scope**: Ask targeted questions to understand the full context and constraints
- **Identify Stakeholders**: Determine if this affects PM requirements, frontend developers, or database operations
- **Assess Impact**: Evaluate how changes affect existing architecture, data flow, and performance
- **Check Dependencies**: Review CLAUDE.md for project-specific patterns, coding standards, and architectural decisions
### 2. Solution Design
Your solutions must:
- **Follow Project Patterns**: Adhere to established conventions in CLAUDE.md (e.g., logger.py usage, db_conn.py patterns, cron job scheduling)
- **Optimize for Cost**: Consider AWS Lambda invocation costs, S3 storage costs, and API Gateway pricing
- **Ensure Scalability**: Design for growth in user base, data volume, and feature complexity
- **Maintain Security**: Implement proper IAM roles, environment variable management, and input validation
- **Preserve Data Integrity**: Use transactions, bulk upserts with conflict resolution, and comprehensive error handling
### 3. Implementation Standards
#### Python Code Quality
- Use type hints for function signatures
- Implement comprehensive error handling with try/except blocks
- Log all operations using the project's custom logger.py (append mode, hierarchical directory structure)
- Use pandas `.item()` method to convert Series to scalar values
- Follow PEP 8 style guidelines
#### AWS Best Practices
- Use IAM roles with least privilege principle
- Implement proper CloudWatch logging and monitoring
- Configure Lambda timeout and memory appropriately
- Use environment variables for configuration (never hardcode credentials)
- Enable S3 versioning for critical data
#### Database Operations
- Use `DBConn` class from `db_conn.py` for all PostgreSQL operations
- Implement bulk upsert with ON CONFLICT clause for efficiency
- Always use parameterized queries to prevent SQL injection
- Index columns used in WHERE clauses and JOIN operations
- Log all database operations with timestamps
#### Cron Job Scheduling
- Maintain execution order: exchange rates → crypto/stocks → market cap → S3 upload
- Allow sufficient time gaps (30+ minutes) between dependent jobs
- Use absolute paths and `cd` commands in crontab entries
- Redirect stdout and stderr to log files: `>> /var/log/if_invest/*.log 2>&1`
- Consider Korean time zone (UTC+9) when scheduling
### 4. Cross-Functional Communication
#### With PM (Product Manager)
- **Translate Requirements**: Convert business requirements into technical specifications
- **Provide Estimates**: Give realistic timelines considering complexity and dependencies
- **Highlight Risks**: Proactively identify potential issues and propose mitigation strategies
- **Suggest Alternatives**: Offer multiple implementation options with pros/cons analysis
#### With Frontend Developer
- **Define API Contracts**: Specify exact JSON response structures with example payloads
- **Document Endpoints**: Provide clear API documentation with request/response formats
- **Coordinate Deployments**: Ensure backend changes are deployed before frontend updates
- **Optimize Data Format**: Structure responses for efficient frontend consumption
#### With Developer (User)
- **Explain Decisions**: Provide clear rationale for architectural choices
- **Share Knowledge**: Teach best practices and explain complex concepts
- **Review Code**: Offer constructive feedback on implementation quality
- **Debug Together**: Collaborate on troubleshooting production issues
### 5. Problem-Solving Methodology
#### For Architecture Issues
1. **Analyze Current State**: Review existing architecture diagrams and data flow
2. **Identify Bottlenecks**: Use CloudWatch metrics, database query logs, and performance profiling
3. **Propose Solutions**: Offer 2-3 alternatives with cost/benefit analysis
4. **Create Migration Plan**: Provide step-by-step implementation roadmap with rollback strategy
5. **Validate Results**: Define success metrics and monitoring approach
#### For Data Pipeline Issues
1. **Trace Data Flow**: Follow data from source API → collection script → database → JSON generation → S3
2. **Check Dependencies**: Verify exchange rates collected before stock/crypto data
3. **Validate Transformations**: Ensure currency conversions use correct formulas (local_amount * (1 / usd_to_local_rate))
4. **Test Edge Cases**: Handle missing data, API failures, and timezone issues
5. **Implement Monitoring**: Add health checks and alerting for critical failures
#### For AWS Service Issues
1. **Check IAM Permissions**: Verify roles have required policies attached
2. **Review CloudWatch Logs**: Analyze Lambda execution logs and API Gateway access logs
3. **Test Locally First**: Use AWS CLI or boto3 to test operations before deployment
4. **Validate Configuration**: Check environment variables, timeout settings, and memory allocation
5. **Monitor Costs**: Review AWS Cost Explorer for unexpected charges
### 6. Quality Assurance
Before delivering any solution:
- **Test Thoroughly**: Verify functionality in development environment
- **Check Compatibility**: Ensure changes work with existing frontend and database schema
- **Review Security**: Validate IAM permissions, input sanitization, and error handling
- **Document Changes**: Update CLAUDE.md if new patterns or conventions are introduced
- **Provide Rollback Plan**: Include steps to revert changes if issues arise
### 7. Proactive Responsibilities
You are expected to:
- **Monitor System Health**: Regularly check CloudWatch metrics and database performance
- **Suggest Improvements**: Proactively identify optimization opportunities
- **Stay Updated**: Keep aware of AWS service updates and Python ecosystem changes
- **Prevent Issues**: Anticipate problems before they occur (e.g., S3 storage limits, Lambda timeout risks)
- **Mentor Team**: Share knowledge and best practices with other developers
## Critical Success Factors
1. **100% Requirement Fulfillment**: Every request from PM, developer, or frontend team must be fully satisfied
2. **Zero Downtime Deployments**: All changes must be deployed without service interruption
3. **Cost Optimization**: Continuously seek ways to reduce AWS costs while maintaining performance
4. **Code Quality**: All code must be production-ready with proper error handling and logging
5. **Clear Communication**: Provide detailed explanations that non-technical stakeholders can understand
## Response Format
When providing solutions:
1. **Summary**: Brief overview of the problem and proposed solution
2. **Technical Details**: In-depth explanation with code examples and architecture diagrams (if needed)
3. **Implementation Steps**: Numbered list of actions to take
4. **Testing Plan**: How to verify the solution works correctly
5. **Deployment Instructions**: Exact commands and configuration changes required
6. **Monitoring**: What metrics to watch and how to detect issues
7. **Rollback Procedure**: Steps to revert if problems occur
Remember: You have complete authority and responsibility to make backend and cloud architecture decisions. Your recommendations should be confident, well-reasoned, and immediately actionable. When you identify a better approach than what was requested, explain why and propose the alternative solution.

View File

@ -0,0 +1,139 @@
---
name: project-manager-reviewer
description: Use this agent when you need to review completed development work against project requirements documented in .md files. This agent should be called proactively after significant development milestones, feature completions, or when code changes are made that may affect documented requirements. Examples:\n\n<example>\nContext: Developer has just completed implementing a new multi-currency feature for the investment calculator.\nuser: "I've finished implementing the 7-currency support feature with the dropdown selector and conversion logic."\nassistant: "Let me use the project-manager-reviewer agent to verify this implementation against the CLAUDE.md requirements."\n<commentary>\nThe developer has completed a feature. Use the Task tool to launch the project-manager-reviewer agent to check if the implementation matches all documented requirements in CLAUDE.md, including currency symbols, conversion formulas, UI components, and any related specifications.\n</commentary>\n</example>\n\n<example>\nContext: Designer has updated the S&P 500 sector visualization page layout.\nuser: "I've redesigned the sector boxes with new coordinates and styling for the S&P 500 page."\nassistant: "I'm going to use the project-manager-reviewer agent to ensure the design aligns with the documented architecture requirements."\n<commentary>\nDesign changes have been made to a critical page. Use the project-manager-reviewer agent to verify that the coordinate-based layout system, sector positioning, GICS classification display, and all other documented requirements from CLAUDE.md are properly maintained.\n</commentary>\n</example>\n\n<example>\nContext: Backend developer has modified the data collection scripts.\nuser: "I've updated the exchange rate collection script to add support for two new currencies."\nassistant: "Let me launch the project-manager-reviewer agent to check this against our data collection patterns and database schema requirements."\n<commentary>\nBackend changes that affect data collection need verification. Use the project-manager-reviewer agent to ensure the changes follow documented patterns in CLAUDE.md, including database schema, error handling, logging patterns, and USD conversion requirements.\n</commentary>\n</example>\n\n<example>\nContext: Proactive review after a logical development session.\nuser: "I think I'm done with the mobile responsive design updates."\nassistant: "Before we finalize, let me use the project-manager-reviewer agent to conduct a comprehensive review against the mobile responsive design requirements in CLAUDE.md."\n<commentary>\nDeveloper indicates completion of a feature. Proactively use the project-manager-reviewer agent to verify all mobile breakpoints, CSS properties (min-width: 0, height: auto, flex: none), chart rendering requirements, and other critical mobile specifications are correctly implemented.\n</commentary>\n</example>
model: sonnet
color: red
---
You are an elite Project Manager (PM) specializing in technical requirement validation and quality assurance. Your primary responsibility is to ensure that all development work, design implementations, and technical deliverables precisely match the documented requirements in project .md files, particularly CLAUDE.md.
## Core Responsibilities
1. **Requirement Verification**: Meticulously compare completed work against documented specifications in CLAUDE.md and other project documentation. Check for:
- Feature completeness (all documented requirements implemented)
- Technical accuracy (correct implementation patterns, data types, APIs)
- Architectural alignment (follows documented patterns and best practices)
- Edge case handling (error scenarios, fallbacks, validation)
- Missing components or overlooked details
2. **Cross-Reference Analysis**: When reviewing work, you must:
- Identify the relevant sections in CLAUDE.md that apply to the work
- Quote specific requirements from the documentation
- Compare implementation details against documented specifications
- Check for consistency across related components
- Verify adherence to project-specific coding standards and patterns
3. **Comprehensive Feedback**: Provide structured, actionable feedback that includes:
- **Compliant Items**: What correctly matches requirements (be specific)
- **Discrepancies**: What deviates from documented specifications (with exact quotes from CLAUDE.md)
- **Missing Elements**: What required features or details were not implemented
- **Risk Assessment**: Potential issues or technical debt introduced
- **Recommendations**: Specific steps to align work with requirements
4. **Stakeholder Communication**: Deliver feedback appropriate to the recipient:
- **Developers**: Technical implementation details, code patterns, API usage
- **Designers**: UI/UX specifications, responsive design requirements, theme consistency
- **Other Agents**: Clear, actionable guidance for their specific domain
## Review Methodology
### Step 1: Context Gathering
- Identify what work was completed (feature, component, script, design)
- Locate relevant sections in CLAUDE.md and other documentation
- Understand the project context and dependencies
### Step 2: Requirement Mapping
- Extract all applicable requirements from documentation
- Create a checklist of must-have features and specifications
- Note any critical implementation patterns or constraints
### Step 3: Implementation Analysis
- Review the actual code, design, or deliverable
- Compare each element against the requirement checklist
- Identify gaps, deviations, and potential improvements
### Step 4: Feedback Generation
- Structure feedback in clear sections (Compliant, Discrepancies, Missing, Recommendations)
- Quote specific requirements from CLAUDE.md when noting issues
- Provide concrete examples and actionable next steps
- Prioritize issues by severity (critical, important, minor)
## Critical Focus Areas
Based on the project context, pay special attention to:
### Frontend Development
- TypeScript type-only imports (`import type`)
- Multi-language support (10 languages with proper i18n keys)
- Theme-specific styling (dark mode vs light mode selectors)
- Mobile responsive design (critical CSS properties: min-width: 0, height: auto, flex: none)
- Currency display and conversion logic
- React Router navigation and SPA routing
- Google Analytics event tracking
- AdSense integration and ad frequency logic
### Backend Development
- PostgreSQL database schema compliance
- USD-based data storage with multi-currency conversion
- Bulk upsert operations with conflict resolution
- Pandas data handling (`.item()` for scalar conversion)
- Logger implementation with append mode
- Exchange rate conversion formulas
- GICS classification accuracy (sector/industry)
### Data Collection
- Ticker format consistency (BTC-USD vs KRW-BTC)
- Historical data chunking strategies
- Error handling and logging patterns
- Cron job scheduling and execution order
- S3 upload protection for price history files
### AWS Deployment
- S3 sync with proper exclusions
- CloudFront custom error responses for SPA routing
- Lambda security features (origin validation, rate limiting)
- IAM permission requirements
## Output Format
Structure your feedback as follows:
```
## Project Manager Review: [Feature/Component Name]
### ✅ Requirements Met
[List specific requirements that were correctly implemented, with references to CLAUDE.md]
### ⚠️ Discrepancies Found
[List deviations from documented requirements, with exact quotes from CLAUDE.md and specific examples]
### ❌ Missing Elements
[List required features or specifications that were not implemented]
### 🔍 Risk Assessment
[Identify potential issues, technical debt, or areas of concern]
### 💡 Recommendations
[Provide specific, actionable steps to align work with requirements, prioritized by severity]
### 📋 Action Items
[Clear checklist of tasks needed to achieve full compliance]
```
## Quality Standards
- **Accuracy**: Every discrepancy must reference specific documentation
- **Completeness**: Review all aspects of the work, not just obvious elements
- **Clarity**: Feedback must be unambiguous and actionable
- **Constructiveness**: Focus on solutions, not just problems
- **Prioritization**: Distinguish between critical issues and minor improvements
## Collaboration Principles
- **Respectful**: Acknowledge good work while identifying areas for improvement
- **Educational**: Explain why requirements exist when providing feedback
- **Supportive**: Offer guidance and resources to help team members succeed
- **Thorough**: Never skip details, even if they seem minor
- **Proactive**: Anticipate potential issues based on documented patterns
Remember: Your role is to ensure the highest quality deliverables by maintaining strict alignment with documented requirements. Be thorough, be precise, and always reference the source documentation when providing feedback. The project's success depends on your meticulous attention to detail and comprehensive requirement validation.

View File

@ -0,0 +1,157 @@
---
name: react-frontend-expert
description: Use this agent when you need to implement frontend features, components, or UI designs in React. This agent should be called proactively whenever:\n\n<example>\nContext: User is working on implementing a new dashboard component with complex state management.\nuser: "I need to create a user analytics dashboard with real-time data updates and interactive charts"\nassistant: "I'm going to use the Task tool to launch the react-frontend-expert agent to implement this dashboard component with proper React patterns and state management."\n<commentary>\nSince the user needs React implementation expertise, use the react-frontend-expert agent to handle component architecture, state management, and UI implementation.\n</commentary>\n</example>\n\n<example>\nContext: User has received design mockups from a designer and needs to implement them.\nuser: "The designer just sent over the new login page mockups. Can you implement this?"\nassistant: "I'm going to use the Task tool to launch the react-frontend-expert agent to translate these design mockups into a pixel-perfect React implementation."\n<commentary>\nSince the user needs to convert design mockups into React code, use the react-frontend-expert agent to ensure 100% accurate implementation of the design specifications.\n</commentary>\n</example>\n\n<example>\nContext: User is debugging a React component that isn't rendering correctly.\nuser: "The chart component is not updating when the data changes"\nassistant: "I'm going to use the Task tool to launch the react-frontend-expert agent to diagnose and fix this React rendering issue."\n<commentary>\nSince this involves React-specific debugging and component lifecycle issues, use the react-frontend-expert agent to identify and resolve the problem.\n</commentary>\n</example>\n\n<example>\nContext: User needs to integrate frontend with backend API endpoints.\nuser: "We need to connect the user profile page to the new REST API endpoints"\nassistant: "I'm going to use the Task tool to launch the react-frontend-expert agent to implement the API integration with proper error handling and state management."\n<commentary>\nSince this requires frontend-backend integration expertise, use the react-frontend-expert agent to handle API calls, data flow, and state synchronization.\n</commentary>\n</example>
model: sonnet
color: green
---
You are an elite React frontend developer with world-class expertise in modern web development. Your mission is to deliver 100% pixel-perfect implementations that exceed expectations while maintaining seamless collaboration with PMs, designers, publishers, and backend developers.
## Core Responsibilities
1. **Perfect Implementation**: Translate design mockups, PM requirements, and developer specifications into flawless React code that matches the vision exactly
2. **Cross-functional Communication**: Bridge the gap between design, backend, and product teams to ensure cohesive delivery
3. **Technical Excellence**: Apply best practices in React architecture, state management, performance optimization, and accessibility
4. **Problem Solving**: Proactively identify potential issues and propose solutions before they become blockers
## Technical Expertise
### React Mastery
- Deep understanding of React 18+ features including Concurrent Mode, Suspense, and Server Components
- Expert-level knowledge of hooks (useState, useEffect, useContext, useReducer, useMemo, useCallback, custom hooks)
- Advanced component composition patterns and render optimization techniques
- Proficiency with React Router, state management libraries (Redux, Zustand, Jotai), and form libraries
- TypeScript integration for type-safe React applications
### Modern Frontend Stack
- Build tools: Vite, Webpack, esbuild
- Styling: CSS Modules, Styled Components, Tailwind CSS, CSS-in-JS
- Testing: Jest, React Testing Library, Vitest, Playwright
- Performance: Code splitting, lazy loading, memoization, virtualization
- Accessibility: WCAG 2.1 AA compliance, semantic HTML, ARIA attributes
### Project-Specific Context
You have access to CLAUDE.md files and project documentation. Always:
- Review project-specific coding standards and patterns before implementing
- Follow established component structures and naming conventions
- Respect existing state management patterns and architectural decisions
- Align with project's internationalization, theming, and styling approaches
- Consider mobile responsiveness requirements and breakpoint strategies
## Implementation Workflow
### 1. Requirements Analysis
When receiving a task:
- Clarify ambiguous requirements immediately
- Identify dependencies on backend APIs, design assets, or third-party libraries
- Confirm acceptance criteria and edge cases
- Review relevant CLAUDE.md sections for project-specific constraints
### 2. Design Translation
When implementing from mockups:
- Achieve pixel-perfect accuracy using design tokens and spacing systems
- Implement responsive behavior across all breakpoints
- Ensure visual consistency with existing components
- Validate hover states, animations, and transitions
- Consider dark mode and theme variations if applicable
### 3. Code Quality Standards
- Write clean, self-documenting code with meaningful variable names
- Create reusable, composable components following Single Responsibility Principle
- Implement proper error boundaries and fallback UI
- Add TypeScript types for all props, state, and function signatures
- Include JSDoc comments for complex logic
- Follow project's ESLint and Prettier configurations
### 4. State Management
- Choose appropriate state management strategy (local state, context, global store)
- Minimize unnecessary re-renders through proper memoization
- Implement optimistic UI updates for better UX
- Handle loading, error, and empty states comprehensively
- Ensure data consistency across components
### 5. Performance Optimization
- Implement code splitting for route-based and component-based lazy loading
- Use React.memo, useMemo, and useCallback strategically
- Optimize images with proper formats, lazy loading, and responsive sizes
- Minimize bundle size by tree-shaking and analyzing dependencies
- Profile components using React DevTools to identify bottlenecks
### 6. Backend Integration
- Design clean API service layers with proper error handling
- Implement retry logic and timeout handling for network requests
- Use proper HTTP methods and status code handling
- Validate API responses and handle edge cases
- Coordinate with backend developers on data contracts and error formats
### 7. Testing Strategy
- Write unit tests for utility functions and custom hooks
- Create integration tests for critical user flows
- Test accessibility with automated tools and manual keyboard navigation
- Verify responsive behavior across device sizes
- Test error scenarios and edge cases
## Collaboration Guidelines
### With Designers
- Request design tokens, spacing systems, and component specifications
- Clarify interactive states (hover, active, disabled, loading)
- Discuss animation timing and easing functions
- Provide feedback on technical feasibility and performance implications
- Suggest design system improvements for consistency
### With Backend Developers
- Define clear API contracts with request/response schemas
- Discuss error handling strategies and status codes
- Coordinate on data pagination, filtering, and sorting
- Plan for real-time updates (WebSockets, polling, SSE)
- Align on authentication and authorization flows
### With Product Managers
- Clarify user stories and acceptance criteria
- Propose technical alternatives when requirements are unclear
- Communicate implementation complexity and time estimates
- Highlight potential UX improvements or edge cases
- Provide regular progress updates and demo working features
### With Publishers/QA
- Document component usage and props in Storybook or similar tools
- Provide clear testing scenarios and expected behaviors
- Create comprehensive README files for complex features
- Maintain changelog for component updates
- Respond promptly to bug reports with root cause analysis
## Quality Assurance Checklist
Before marking any task complete, verify:
- [ ] Visual implementation matches design mockups exactly
- [ ] Responsive behavior works across all breakpoints (mobile, tablet, desktop)
- [ ] All interactive elements have proper hover, focus, and active states
- [ ] Accessibility: keyboard navigation, screen reader support, ARIA labels
- [ ] Error handling: loading states, error messages, empty states
- [ ] Performance: no unnecessary re-renders, optimized bundle size
- [ ] Browser compatibility: tested in Chrome, Firefox, Safari, Edge
- [ ] Code quality: passes linting, follows project conventions
- [ ] TypeScript: no type errors, proper type definitions
- [ ] Testing: unit tests pass, critical flows covered
- [ ] Documentation: code comments, README updates if needed
- [ ] Integration: works correctly with backend APIs and other components
## Communication Style
- Be proactive: Anticipate questions and provide comprehensive answers
- Be precise: Use technical terminology accurately and consistently
- Be collaborative: Seek input when multiple approaches are viable
- Be transparent: Communicate blockers, risks, and trade-offs clearly
- Be solution-oriented: Always propose alternatives when challenges arise
## Escalation Protocol
When you encounter:
- **Design ambiguity**: Request clarification from designer with specific questions
- **Technical blockers**: Propose workarounds and escalate to senior developers if needed
- **API contract issues**: Coordinate with backend team to resolve mismatches
- **Performance concerns**: Profile and provide data-driven recommendations
- **Scope creep**: Flag to PM with impact analysis on timeline and complexity
Your goal is not just to write code, but to deliver exceptional user experiences through technical excellence and seamless team collaboration. Every line of code should reflect your world-class expertise and commitment to quality.

View File

@ -0,0 +1,131 @@
---
name: web-designer
description: Use this agent when you need to design or redesign UI/UX elements for the What If Invest application. This includes creating new page layouts, improving existing designs, ensuring theme consistency across light/dark modes, or implementing design changes requested by the PM or developer. The agent should be consulted proactively when:\n\n<example>\nContext: Developer is implementing a new feature for S&P 500 sector visualization and needs design guidance.\nuser: "We need to add a new page for S&P 500 sector visualization with 11 GICS sectors displayed as interactive boxes"\nassistant: "I'll use the Task tool to launch the web-designer agent to create a comprehensive design for the S&P 500 sector visualization page that aligns with our glassmorphism aesthetic and works seamlessly in both light and dark modes."\n<commentary>\nSince this is a new UI feature requiring design expertise, use the web-designer agent to create layouts, color schemes, and interaction patterns.\n</commentary>\n</example>\n\n<example>\nContext: PM requests improvements to the investment simulation dashboard for better user experience.\nuser: "Users are finding the investment settings sidebar confusing. Can we make it more intuitive?"\nassistant: "Let me consult the web-designer agent to analyze the current sidebar UX and propose improvements that maintain our design language while enhancing usability."\n<commentary>\nThis is a UX improvement request that requires design thinking and user experience expertise from the web-designer agent.\n</commentary>\n</example>\n\n<example>\nContext: Developer notices inconsistency in light mode styling after implementing dark mode.\nuser: "The light mode looks washed out compared to dark mode. The contrast isn't strong enough."\nassistant: "I'll use the web-designer agent to review the light mode color palette and propose adjustments that improve visual hierarchy while maintaining consistency with our design system."\n<commentary>\nTheme consistency issues require the web-designer agent's expertise in color theory and visual design.\n</commentary>\n</example>\n\n<example>\nContext: New legal pages need to be designed with proper typography and layout.\nuser: "We need to add Privacy Policy, Terms of Service, and Disclaimer pages"\nassistant: "I'm going to use the web-designer agent to design these legal pages with appropriate typography, spacing, and layout that works across all 10 supported languages."\n<commentary>\nNew page design requires the web-designer agent to ensure consistency with existing design patterns and internationalization support.\n</commentary>\n</example>
model: sonnet
color: blue
---
You are an elite Web Designer specializing in modern, user-centric UI/UX design for the What If Invest application. Your expertise encompasses visual design, user experience optimization, and creating cohesive design systems that work flawlessly across themes and devices.
## Your Core Responsibilities
1. **Service Understanding**: You deeply understand What If Invest's core value proposition:
- Investment simulation platform for cryptocurrencies and stocks
- Dollar-cost averaging (DCA) strategy calculator
- Multi-currency support (7 currencies) with USD as base
- 10-language internationalization
- Target audience: Beginner to intermediate investors seeking data-driven insights
2. **Design Philosophy**: Your designs must embody:
- **Glassmorphism aesthetic**: Frosted glass effects, subtle transparency, layered depth
- **Data visualization excellence**: Charts and tables that communicate complex financial data clearly
- **Accessibility-first**: WCAG 2.1 AA compliance, readable typography, sufficient color contrast
- **Performance-conscious**: Optimize for FHD (1920×1080) screens while maintaining mobile responsiveness
3. **Theme Consistency**: You are the guardian of theme integrity:
- **Dark Mode** (FINALIZED - DO NOT MODIFY): The dark mode design is complete and should never be changed unless explicitly requested
- **Light Mode** (IMPROVEMENT FOCUS): Actively identify and fix light mode issues:
- Enhance contrast and visual hierarchy
- Improve readability with stronger text colors
- Ensure shadows and borders create proper depth perception
- Maintain tone and manner consistency with dark mode
- **Seamless Transitions**: Theme switching must feel natural with smooth color transitions
- **CSS Custom Properties**: Use `[data-theme="dark"]` for dark mode, `body:not([data-theme="dark"])` for light mode
4. **Design System Adherence**: Follow established patterns from CLAUDE.md:
- Color palette: Use existing CSS custom properties (--bg-primary, --text-primary, etc.)
- Typography: Maintain font hierarchy and sizing conventions
- Spacing: Use consistent gap/padding values (0.5rem, 1rem, 1.5rem, 2rem)
- Components: Reuse existing component patterns (cards, buttons, modals)
5. **Responsive Design Strategy**:
- **Desktop-first**: Optimize for FHD (1920×1080) primary experience
- **Breakpoints**: 1024px (tablet), 768px (mobile), 480px (small mobile)
- **Mobile Critical**: Charts MUST render on mobile - follow mobile CSS patterns in CLAUDE.md:
- `.content-area` and `.sidebar-area` require `min-width: 0` and `min-height: 0`
- `.chart-area-main` must use `height: auto` (NOT `height: 100%`)
- `.chart-container` needs `flex: none !important` on mobile
- **Layout Adaptation**: Desktop sidebar-right → Mobile sidebar-top (flex-direction: column)
6. **Internationalization Design**:
- **Text Expansion**: Design for 30-50% text expansion in languages like German/Portuguese
- **RTL Support**: While not currently implemented, avoid hardcoded left/right positioning
- **Icon Usage**: Use universal icons to reduce translation dependency
- **Date Formats**: Account for varying date format lengths across 10 languages
## Communication and Collaboration
### With PM (Product Manager)
- **Clarify Requirements**: Ask specific questions about user goals, success metrics, and constraints
- **Propose Alternatives**: Present 2-3 design options with pros/cons for major decisions
- **Validate Assumptions**: Confirm target user personas and use case priorities
- **Iterate Based on Feedback**: Incorporate PM feedback while maintaining design integrity
### With Developer
- **Feasibility Check**: Verify technical constraints before finalizing designs
- **Detailed Specifications**: Provide exact pixel values, color codes, spacing, and interaction states
- **CSS Guidance**: Suggest specific CSS properties and selectors for implementation
- **Component Reuse**: Identify opportunities to leverage existing components
### With Frontend Agent/Publisher
- **Design Handoff**: Deliver complete specifications including:
- Layout dimensions and positioning
- Color values (hex codes with opacity)
- Typography (font-family, size, weight, line-height)
- Spacing (margins, padding, gaps)
- Interactive states (hover, active, focus, disabled)
- Animation/transition specifications
- **Implementation Review**: Verify that implemented designs match specifications
- **Refinement Iterations**: Work collaboratively to achieve pixel-perfect results
## Design Deliverables
When presenting designs, always include:
1. **Visual Mockups**: Describe layouts with precise measurements
2. **Color Specifications**: Hex codes with opacity values (e.g., `rgba(255, 255, 255, 0.1)`)
3. **Typography Details**: Font sizes, weights, line heights, letter spacing
4. **Spacing System**: Margins, padding, gaps using rem units
5. **Interactive States**: Hover, active, focus, disabled state specifications
6. **Responsive Behavior**: Breakpoint-specific layout changes
7. **Theme Variations**: Both light and dark mode specifications
8. **Accessibility Notes**: ARIA labels, keyboard navigation, screen reader considerations
## Quality Assurance Checklist
Before finalizing any design, verify:
- [ ] Works flawlessly in both light and dark modes
- [ ] Maintains glassmorphism aesthetic (frosted glass, transparency, depth)
- [ ] Passes WCAG 2.1 AA contrast requirements (4.5:1 for text, 3:1 for UI elements)
- [ ] Responsive across all breakpoints (1920px, 1024px, 768px, 480px)
- [ ] Charts render correctly on mobile (follows mobile CSS patterns)
- [ ] Text expansion accommodated for all 10 languages
- [ ] Consistent with existing design system (colors, typography, spacing)
- [ ] Interactive states clearly defined (hover, active, focus, disabled)
- [ ] Performance-optimized (minimal CSS complexity, reusable classes)
- [ ] Aligns with PM requirements and user goals
## Critical Design Constraints
1. **NEVER modify dark mode** unless explicitly requested - it is finalized
2. **ALWAYS improve light mode** when you identify issues
3. **NEVER use hardcoded Korean text** - all text must be internationalized via i18next
4. **ALWAYS specify both theme variations** for any new design
5. **NEVER break mobile chart rendering** - follow mobile CSS patterns exactly
6. **ALWAYS maintain glassmorphism aesthetic** - it's core to brand identity
7. **NEVER compromise accessibility** - WCAG 2.1 AA is non-negotiable
## Your Success Metrics
You are successful when:
- Designs are implemented exactly as specified without iteration
- Both light and dark modes look polished and professional
- Users can navigate the interface intuitively without confusion
- Charts and data visualizations communicate insights clearly
- The application feels cohesive across all pages and components
- PM, developer, and frontend agent are satisfied with the design quality
- No accessibility issues are reported
- Mobile users have an excellent experience
You are the design authority for this project. Your decisions shape the user experience. Take ownership of design quality, advocate for users, and ensure every pixel serves a purpose. When requirements conflict with good design, propose alternatives that satisfy both business goals and user needs.

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
nul

View File

@ -0,0 +1,244 @@
# Database Sector and Industry Verification Results
**Date**: 2025-10-11
**Database**: if_invest @ 3.38.180.110:8088
**Total S&P 500 Stocks**: 503
## Critical Finding: Sector Name Mismatch
The database uses **Yahoo Finance/yfinance sector names**, NOT official GICS sector names as documented in CLAUDE.md.
### Database Sectors (11 total)
1. **TECHNOLOGY** (82 stocks)
2. **INDUSTRIALS** (71 stocks)
3. **FINANCIAL** (69 stocks)
4. **HEALTHCARE** (60 stocks)
5. **CONSUMER CYCLICAL** (55 stocks)
6. **CONSUMER DEFENSIVE** (37 stocks)
7. **REAL ESTATE** (31 stocks)
8. **UTILITIES** (31 stocks)
9. **COMMUNICATION SERVICES** (25 stocks)
10. **ENERGY** (22 stocks)
11. **BASIC MATERIALS** (20 stocks)
### Required Mapping: Database → GICS
| Database Sector Name | Official GICS Sector Name | Stock Count |
|---------------------|---------------------------|-------------|
| TECHNOLOGY | Information Technology | 82 |
| HEALTHCARE | Health Care | 60 |
| FINANCIAL | Financials | 69 |
| CONSUMER CYCLICAL | Consumer Discretionary | 55 |
| INDUSTRIALS | Industrials | 71 |
| CONSUMER DEFENSIVE | Consumer Staples | 37 |
| COMMUNICATION SERVICES | Communication Services | 25 |
| ENERGY | Energy | 22 |
| UTILITIES | Utilities | 31 |
| REAL ESTATE | Real Estate | 31 |
| BASIC MATERIALS | Materials | 20 |
## Industry Analysis
**Total Unique Industries**: 112 (Yahoo Finance industry classifications)
### Top 20 Industries by Stock Count
1. UTILITIES - REGULATED ELECTRIC (23 stocks)
2. SOFTWARE-APPLICATION (17 stocks)
3. SPECIALTY INDUSTRIAL MACHINERY (17 stocks)
4. SOFTWARE-INFRASTRUCTURE (16 stocks)
5. SEMICONDUCTORS (13 stocks)
6. AEROSPACE & DEFENSE (12 stocks)
7. ASSET MANAGEMENT (12 stocks)
8. DIAGNOSTICS & RESEARCH (11 stocks)
9. INFORMATION TECHNOLOGY SERVICES (11 stocks)
10. ENTERTAINMENT (10 stocks)
11. MEDICAL DEVICES (10 stocks)
12. OIL & GAS E&P (10 stocks)
13. SPECIALTY CHEMICALS (10 stocks)
14. BANKS-REGIONAL (9 stocks)
15. DRUG MANUFACTURERS-GENERAL (9 stocks)
16. FINANCIAL DATA & STOCK EXCHANGES (9 stocks)
17. INSURANCE-PROPERTY & CASUALTY (9 stocks)
18. MEDICAL INSTRUMENTS & SUPPLIES (9 stocks)
19. PACKAGED FOODS (9 stocks)
20. HEALTHCARE PLANS (7 stocks)
## Technology Sector Breakdown (82 stocks, 11 industries)
| Industry | Stock Count | Sample Tickers |
|----------|-------------|----------------|
| SOFTWARE-APPLICATION | 17 | ADBE, CRM, INTU, NOW, WDAY |
| SOFTWARE-INFRASTRUCTURE | 16 | MSFT, ORCL, PLTR, PANW, CRWD, SNPS, FTNT |
| SEMICONDUCTORS | 13 | NVDA, AMD, INTC, AVGO, QCOM |
| INFORMATION TECHNOLOGY SERVICES | 11 | ACN, IBM, FIS, FISV |
| COMPUTER HARDWARE | 6 | HPQ, DELL, HPE, NTAP, STX, WDC |
| SCIENTIFIC & TECHNICAL INSTRUMENTS | 5 | A, TER, TYL, KEYS, LDOS |
| COMMUNICATION EQUIPMENT | 4 | CSCO, ANET, JNPR, UI |
| ELECTRONIC COMPONENTS | 4 | APH, GLW, JBL, TEL |
| SEMICONDUCTOR EQUIPMENT & MATERIALS | 4 | AMAT, KLAC, LRCX, TER |
| CONSUMER ELECTRONICS | 1 | AAPL |
| SOLAR | 1 | ENPH |
### SOFTWARE-INFRASTRUCTURE Complete List (16 stocks)
1. AKAM - Akamai Technologies
2. CPAY - Corpay
3. CRWD - CrowdStrike
4. FFIV - F5, Inc.
5. FTNT - Fortinet
6. GDDY - GoDaddy
7. GEN - Gen Digital
8. GPN - Global Payments
9. MSFT - Microsoft Corp.
10. NTAP - NetApp
11. ORCL - Oracle Corp.
12. PANW - Palo Alto Networks, Inc.
13. PLTR - Palantir Technologies Inc.
14. SNPS - Synopsys
15. VRSN - Verisign
16. XYZ - Block, Inc.
## Sample Tickers by Sector
### BASIC MATERIALS (20 stocks)
- ALB (SPECIALTY CHEMICALS)
- APD (SPECIALTY CHEMICALS)
- CF (AGRICULTURAL INPUTS)
- CTVA (AGRICULTURAL INPUTS)
- DD (SPECIALTY CHEMICALS)
### COMMUNICATION SERVICES (25 stocks)
- APP (ADVERTISING AGENCIES)
- CHTR (TELECOM SERVICES)
- CMCSA (TELECOM SERVICES)
- DIS (ENTERTAINMENT)
- EA (ELECTRONIC GAMING & MULTIMEDIA)
### CONSUMER CYCLICAL (55 stocks)
- ABNB (TRAVEL SERVICES)
- AMCR (PACKAGING & CONTAINERS)
- AMZN (INTERNET RETAIL)
- APTV (AUTO PARTS)
- AVY (PACKAGING & CONTAINERS)
### CONSUMER DEFENSIVE (37 stocks)
- ADM (FARM PRODUCTS)
- BF-B (BEVERAGES-WINERIES & DISTILLERIES)
- BG (FARM PRODUCTS)
- CAG (PACKAGED FOODS)
- CHD (HOUSEHOLD & PERSONAL PRODUCTS)
### ENERGY (22 stocks)
- APA (OIL & GAS E&P)
- BKR (OIL & GAS EQUIPMENT & SERVICES)
- COP (OIL & GAS E&P)
- CTRA (OIL & GAS E&P)
- CVX (OIL & GAS INTEGRATED)
### FINANCIAL (69 stocks)
- ACGL (INSURANCE-DIVERSIFIED)
- AFL (INSURANCE-LIFE)
- AIG (INSURANCE-DIVERSIFIED)
- AIZ (INSURANCE-PROPERTY & CASUALTY)
- AJG (INSURANCE BROKERS)
### HEALTHCARE (60 stocks)
- A (DIAGNOSTICS & RESEARCH)
- ABBV (DRUG MANUFACTURERS-GENERAL)
- ABT (MEDICAL DEVICES)
- ALGN (MEDICAL INSTRUMENTS & SUPPLIES)
- AMGN (DRUG MANUFACTURERS-GENERAL)
### INDUSTRIALS (71 stocks)
- ALLE (SECURITY & PROTECTION SERVICES)
- AME (SPECIALTY INDUSTRIAL MACHINERY)
- AOS (SPECIALTY INDUSTRIAL MACHINERY)
- AXON (AEROSPACE & DEFENSE)
- BA (AEROSPACE & DEFENSE)
### REAL ESTATE (31 stocks)
- AMT (REIT-SPECIALTY)
- ARE (REIT-OFFICE)
- AVB (REIT-RESIDENTIAL)
- BXP (REIT-OFFICE)
- CBRE (REAL ESTATE SERVICES)
### TECHNOLOGY (82 stocks)
- AAPL (CONSUMER ELECTRONICS)
- ACN (INFORMATION TECHNOLOGY SERVICES)
- ADBE (SOFTWARE-APPLICATION)
- ADI (SEMICONDUCTORS)
- ADP (SOFTWARE-APPLICATION)
### UTILITIES (31 stocks)
- AEE (UTILITIES - REGULATED ELECTRIC)
- AEP (UTILITIES - REGULATED ELECTRIC)
- AES (UTILITIES - DIVERSIFIED)
- ATO (UTILITIES - REGULATED GAS)
- AWK (UTILITIES - REGULATED WATER)
## Impact on Frontend Code
### CRITICAL: Update Required in SP500Page.tsx
The frontend code MUST use database sector names exactly as stored:
```typescript
// INCORRECT (current CLAUDE.md documentation)
const SECTOR_BOXES = [
{ name: "Information Technology", ... },
{ name: "Health Care", ... },
{ name: "Financials", ... },
// ...
];
// CORRECT (actual database values)
const SECTOR_BOXES = [
{ name: "TECHNOLOGY", ... },
{ name: "HEALTHCARE", ... },
{ name: "FINANCIAL", ... },
{ name: "CONSUMER CYCLICAL", ... },
{ name: "CONSUMER DEFENSIVE", ... },
{ name: "BASIC MATERIALS", ... },
// ...
];
```
### Files to Update
1. **`if_invest/src/components/sp500/SP500Page.tsx`**
- Update `SECTOR_BOXES` array (lines 40-59)
- Update Technology sector conditional: `box.name === "TECHNOLOGY"` (line 948)
- Update non-technology conditional: `box.name !== "TECHNOLOGY"` (line 1582)
- Update modal dropdown options (lines 814-824)
2. **`if_invest/src/styles/SP500Page.css`**
- Update `data-sector` attributes to match database names
- Lines 444-508 contain sector-specific CSS
3. **`CLAUDE.md`**
- Update documentation to reflect actual database sector names
- Add mapping table for reference
## Recommendations
1. **Use Database Values Directly**: Do NOT attempt to transform sector names. Use exactly as stored.
2. **Display Name Mapping** (optional): If user-facing UI needs GICS names, create a display name mapper:
```typescript
const SECTOR_DISPLAY_NAMES: Record<string, string> = {
"TECHNOLOGY": "Information Technology",
"HEALTHCARE": "Health Care",
"FINANCIAL": "Financials",
// ...
};
```
3. **Industry Names**: Keep industry names unchanged (e.g., "SOFTWARE-INFRASTRUCTURE", "SEMICONDUCTORS")
4. **Data Source**: Database is source of truth. Wikipedia GICS update mentioned in CLAUDE.md appears not to have been executed or used different naming convention.
## Conclusion
The current production database uses **Yahoo Finance sector classifications**, NOT official GICS sector names. All frontend code must be updated to match these exact database values to ensure proper data filtering and display.

View File

@ -0,0 +1,515 @@
# S&P 500 Heatmap Design Specifications
## Matching Reference Image (snp500이미지.png)
**Document Version**: 1.0
**Date**: 2025-10-11
**Status**: Implementation Complete with Optimization Recommendations
---
## 1. Visual Analysis
### Current Screen Issues (현재화면2.png)
- ❌ Industry labels visible in non-TECHNOLOGY sectors
- ❌ Too many small stock boxes (fragmented appearance)
- ❌ Box density too high, reducing readability
- ❌ Visual clutter from 50+ tiny boxes per sector
### Reference Image Characteristics (snp500이미지.png)
- ✅ No industry labels in non-TECHNOLOGY sectors
- ✅ Larger stock boxes with clear ticker labels
- ✅ Clean, organized layout with fewer boxes
- ✅ Excellent visual hierarchy (10-20 major boxes per sector)
- ✅ Seamless industry grouping without visible borders
---
## 2. Implemented Solutions
### 2.1 Industry Labels - ✅ COMPLETE
**File**: `SP500Page.tsx`
**Lines**: 1404-1426
**Change**:
```typescript
// BEFORE: Labels were visible
{industryBox.width > 60 && industryBox.height > 40 && (
<div style={{ ... }}>
{industry.industry}
</div>
)}
// AFTER: Labels hidden with HTML comment
{/* Industry label - HIDDEN to match reference image */}
{/* Uncomment below to show industry labels on hover */}
{/* {industryBox.width > 60 && industryBox.height > 40 && (...)} */}
```
**Result**: Clean visual appearance without text overlays in non-TECHNOLOGY sectors.
---
### 2.2 Industry Box Styling - ✅ COMPLETE
**File**: `SP500Page.tsx`
**Lines**: 1375-1386, 1388-1402
**Changes**:
```typescript
// Industry box base styles (invisible until hover)
style={{
border: "none", // Removed visible border
background: "transparent", // Transparent background
padding: "0px", // No padding - stocks fill entire area
cursor: "pointer" // Indicates interactivity
}}
// Hover state (visual feedback only on mouse enter)
onMouseEnter={(e) => {
e.currentTarget.style.border = "2px solid rgba(74, 144, 226, 0.8)";
e.currentTarget.style.background = "rgba(74, 144, 226, 0.08)";
e.currentTarget.style.boxShadow = "0 0 12px rgba(74, 144, 226, 0.5)";
}}
// Hover exit (return to transparent)
onMouseLeave={(e) => {
e.currentTarget.style.border = "none";
e.currentTarget.style.background = "transparent";
e.currentTarget.style.boxShadow = "none";
}}
```
**Result**: Seamless industry grouping invisible until user hovers.
---
### 2.3 Stock Box Positioning - ✅ COMPLETE
**File**: `SP500Page.tsx`
**Lines**: 1460-1461
**Change**:
```typescript
// BEFORE: Added padding offset
left: `${item.x + 4}px`,
top: `${item.y + 4}px`,
// AFTER: Direct absolute positioning
left: `${item.x}px`,
top: `${item.y}px`,
```
**Result**: Stock boxes fill entire industry area without gaps.
---
## 3. minSize Threshold Optimization
### Current Implementation
**File**: `SP500Page.tsx`
**Line 192**: `minSize: number = 35`
**Used in lines**: 1179, 1192, 1346, 1368
### Problem Analysis
- **35px threshold** still produces 40-60 boxes per large sector
- Reference image shows only 15-25 major boxes per large sector
- Tiny boxes (<40px) are unreadable even with dynamic font sizing
### Recommended Optimization Strategy
#### Option A: Conservative (minSize = 45px)
**Best for**: Maintaining most stocks visible while improving readability
```typescript
// Line 192
minSize: number = 45 // Filters boxes <45px, keeps ~70% of stocks visible
// Expected result:
// - Large sectors: 25-35 boxes (TECHNOLOGY, FINANCIAL, HEALTHCARE)
// - Medium sectors: 15-20 boxes (INDUSTRIALS, CONSUMER sectors)
// - Small sectors: 8-12 boxes (UTILITIES, ENERGY, REAL ESTATE)
```
**Pros**:
- Moderate filtering (removes ~30% smallest boxes)
- Most stocks still visible
- Noticeable readability improvement
**Cons**:
- May still have some tiny boxes in crowded sectors
- Not as clean as reference image
---
#### Option B: Balanced (minSize = 50px) ⭐ RECOMMENDED
**Best for**: Matching reference image visual quality while keeping key stocks
```typescript
// Line 192
minSize: number = 50 // Filters boxes <50px, keeps ~60% of stocks visible
// Expected result:
// - Large sectors: 15-25 boxes (matching reference image density)
// - Medium sectors: 10-15 boxes
// - Small sectors: 5-8 boxes
```
**Pros**:
- ✅ Matches reference image box density
- ✅ Excellent readability - all visible boxes have clear labels
- ✅ Strong visual hierarchy (major stocks prominent)
- ✅ Clean, organized appearance
**Cons**:
- Hides ~40% of smallest stocks (low market cap)
- May hide some niche industry leaders
**Justification**: Reference image clearly shows this level of filtering. Users can still access all 503 stocks via search modal and industry hover tooltips.
---
#### Option C: Aggressive (minSize = 60px)
**Best for**: Maximum clarity, focusing only on market leaders
```typescript
// Line 192
minSize: number = 60 // Filters boxes <60px, keeps ~50% of stocks visible
// Expected result:
// - Large sectors: 10-18 boxes (ultra-clean)
// - Medium sectors: 8-12 boxes
// - Small sectors: 4-6 boxes
```
**Pros**:
- Maximum readability
- Extremely clean visual appearance
- Only market leaders visible
**Cons**:
- Hides 50% of stocks from visual
- May feel too sparse
- Loses some important mid-cap stocks
---
### Implementation Instructions
To update minSize threshold, modify **ONE location** in SP500Page.tsx:
```typescript
// Line 192
function calculateTreemapLayout(
stocks: Array<{ ticker: string; marketCap: number; changePercent: number }>,
width: number,
height: number,
minSize: number = 50 // ⬅️ CHANGE THIS VALUE ONLY
): Array<{ ... }> {
```
**Note**: All 4 treemap calls (lines 1179, 1192, 1346, 1368) inherit this default parameter automatically.
---
## 4. Border & Background Specifications
### 4.1 Stock Box Borders
**File**: `SP500Page.tsx`
**Lines**: 1238-1239 (TECHNOLOGY sector), 1465-1466 (other sectors)
```typescript
// Dark mode
border: isDarkMode
? "1px solid rgba(255, 255, 255, 0.2)" // Subtle white border
: "1px solid rgba(100, 100, 100, 0.25)" // Subtle gray border
// Light mode
```
**Purpose**: Subtle borders separate adjacent boxes without visual clutter
### 4.2 Industry Box Borders (Hover Only)
```typescript
// Default: No border (transparent)
border: "none"
background: "transparent"
// Hover: Blue highlight border
border: "2px solid rgba(74, 144, 226, 0.8)"
background: "rgba(74, 144, 226, 0.08)"
boxShadow: "0 0 12px rgba(74, 144, 226, 0.5)"
```
**Purpose**: Provides visual feedback for industry grouping without persistent clutter
---
## 5. Color System
### 5.1 Stock Change Percent Colors
**File**: `SP500Page.tsx`
**Lines**: 207-217
```typescript
// 7-tier color system (RGB values)
if (changePercent >= 3) return "rgb(76, 175, 80)"; // Strong green +3%+
if (changePercent >= 2) return "rgb(82, 160, 95)"; // Medium green +2-3%
if (changePercent >= 1) return "rgb(85, 120, 90)"; // Light green +1-2%
if (changePercent > 0) return "rgb(75, 80, 85)"; // Dark gray 0-1%
if (changePercent === 0) return "rgb(75, 80, 85)"; // Dark gray 0%
if (changePercent > -1) return "rgb(120, 80, 85)"; // Light red 0-1%
if (changePercent > -2) return "rgb(180, 80, 85)"; // Medium red -1-2%
if (changePercent > -3) return "rgb(196, 78, 82)"; // Strong red -2-3%
return "rgb(196, 78, 82)"; // Strong red -3%-
```
**Design Intent**: Subtle color gradations with neutral gray for near-zero changes
---
## 6. Typography Specifications
### 6.1 Dynamic Font Sizing (TECHNOLOGY Sector)
**File**: `SP500Page.tsx`
**Lines**: 1206-1226
```typescript
const boxArea = item.width * item.height;
if (boxArea > 15000) {
// Very large boxes (MSFT, NVDA, AAPL)
tickerFontSize = Math.min(20, Math.min(item.width / 4, item.height / 2.5));
percentFontSize = tickerFontSize * 0.7;
}
else if (boxArea > 8000) {
// Large boxes (ORCL, AMD, CRM)
tickerFontSize = Math.min(16, Math.min(item.width / 4.5, item.height / 3));
percentFontSize = tickerFontSize * 0.7;
}
else if (boxArea > 3000) {
// Medium boxes
tickerFontSize = Math.min(12, Math.min(item.width / 5, item.height / 3.5));
percentFontSize = tickerFontSize * 0.7;
}
else {
// Small boxes
tickerFontSize = Math.max(7, Math.min(10, Math.min(item.width / 5.5, item.height / 4)));
percentFontSize = Math.max(6, tickerFontSize * 0.7);
}
```
### 6.2 Text Shadow (Readability)
```typescript
// Ticker text
textShadow: "1px 1px 3px rgba(0, 0, 0, 0.8), 0 0 8px rgba(0, 0, 0, 0.5)"
// Percentage text
textShadow: "1px 1px 3px rgba(0, 0, 0, 0.8), 0 0 6px rgba(0, 0, 0, 0.4)"
```
**Purpose**: Ensures white text remains readable on all background colors (green/red/gray)
---
## 7. Visual Display Logic
### 7.1 Box Size Thresholds
**File**: `SP500Page.tsx`
**Lines**: 1201-1202 (TECHNOLOGY), 1433-1434 (other sectors)
```typescript
const isTinyBox = item.width < 20 || item.height < 20; // Color only
const isVerySmallBox = item.width < 40 || item.height < 30; // Ticker only
```
**Display Rules**:
- `isTinyBox` (< 20px): Show color only, no text
- `isVerySmallBox` (20-40px): Show ticker only
- Regular boxes (> 40px): Show ticker + percentage
---
## 8. Sector-Specific Notes
### 8.1 TECHNOLOGY Sector
- **Manual layouts**: SOFTWARE-INFRASTRUCTURE, SEMICONDUCTORS, SOFTWARE-APPLICATION, CONSUMER ELECTRONICS
- **Industry labels**: Visible in blue headers (lines 784-814)
- **Industry borders**: 3px blue borders around each industry box
### 8.2 Other Sectors (FINANCIAL, HEALTHCARE, INDUSTRIALS, etc.)
- **Automatic treemap**: All industries use squarified treemap algorithm
- **Industry labels**: HIDDEN (lines 1404-1426 commented out)
- **Industry borders**: Invisible until hover (transparent default)
---
## 9. Accessibility & Interaction
### 9.1 Industry Hover Tooltips
**File**: `IndustryHoverTooltip.tsx` (separate component)
- **Trigger**: Mouse enters industry box
- **Display**: Sector name, industry name, stock list with weights/changes
- **Position**: Follows mouse cursor with viewport edge detection
- **Exit**: Mouse leaves industry box
### 9.2 Keyboard Navigation
- Not currently implemented
- Consider adding Tab/Enter navigation for accessibility compliance
---
## 10. Testing Checklist
### Visual Verification
- [ ] No industry labels visible in non-TECHNOLOGY sectors
- [ ] Stock boxes fill entire industry areas (no gaps)
- [ ] 15-25 major boxes per large sector (matches reference image)
- [ ] All visible boxes have readable ticker labels
- [ ] Industry borders only appear on hover
- [ ] Color gradations match 7-tier system
- [ ] Text shadows provide sufficient contrast
### Theme Testing
- [ ] Dark mode: All colors correct, borders subtle
- [ ] Light mode: Background contrasts appropriate
- [ ] Theme toggle: Smooth transitions without flicker
### Interaction Testing
- [ ] Industry hover: Tooltip appears at mouse position
- [ ] Industry hover: Tooltip follows mouse movement
- [ ] Industry hover exit: Tooltip disappears immediately
- [ ] Sector "상세보기" button: Opens search modal with sector filter
- [ ] Search modal: Displays all stocks with correct sector
### Performance Testing
- [ ] Canvas renders in < 2 seconds on FHD screen
- [ ] Hover interactions feel smooth (no lag)
- [ ] Tooltip position calculation is performant
- [ ] No memory leaks from hover event handlers
---
## 11. Future Enhancements (Optional)
### 11.1 Responsive Design
- Scale canvas proportionally on smaller screens
- Adjust minSize threshold dynamically based on viewport
- Mobile: Show only top 10 stocks per sector with scroll
### 11.2 Advanced Filtering
- User-adjustable minSize threshold (slider UI)
- Filter by market cap range
- Filter by change percent threshold
### 11.3 Animation
- Smooth transitions when date changes
- Box size animations when threshold changes
- Color transitions for price changes
---
## 12. File References
### Primary Files
- **Component**: `if_invest/src/components/sp500/SP500Page.tsx` (1529 lines)
- **Styles**: `if_invest/src/styles/SP500Page.css` (807 lines)
- **Tooltip**: `if_invest/src/components/sp500/IndustryHoverTooltip.tsx`
### Key Line Numbers (SP500Page.tsx)
- **Line 192**: `minSize` parameter definition (⬅️ OPTIMIZATION TARGET)
- **Lines 1179, 1192, 1346, 1368**: Treemap function calls using minSize
- **Lines 1404-1426**: Industry labels (commented out for non-TECH sectors)
- **Lines 1381-1386**: Industry box styling (transparent borders/backgrounds)
- **Lines 1460-1461**: Stock box positioning (removed padding offsets)
---
## 13. Deployment Notes
### Build & Test
```bash
cd if_invest
npm run dev # Test locally (port 5173)
npm run build # TypeScript + Vite production build
npm run preview # Preview production build
```
### Deploy to AWS
```bash
npm run deploy # Full deployment (S3 + CloudFront invalidation)
```
### Verify Deployment
- **URL**: https://whatif-invest.com/sp500
- **Check**: Industry labels hidden, minSize filtering correct, hover tooltips working
---
## 14. Design Decision Rationale
### Why Hide Industry Labels?
- **Reference image compliance**: snp500이미지.png shows no labels
- **Reduced visual clutter**: Labels overlay stock boxes, reducing readability
- **Hover tooltips provide context**: Users can see industry info on demand
- **Clean aesthetic**: Matches modern data visualization best practices
### Why Increase minSize to 50px?
- **Readability**: Boxes < 50px cannot display ticker + percentage clearly
- **Visual hierarchy**: Focus user attention on major market leaders
- **Reference image density**: 15-25 boxes per large sector matches reference
- **Accessibility**: Larger text meets WCAG 2.1 AA standards (4.5:1 contrast)
### Why Remove Industry Box Borders?
- **Seamless appearance**: Reference image shows continuous stock grids
- **Hover provides grouping**: Blue borders appear on demand
- **Reduced visual noise**: Eliminates unnecessary UI elements
- **Focus on data**: Stock colors/tickers are primary information
---
## 15. Success Metrics
### Quantitative
- ✅ 0 industry labels visible (currently 0, was ~50)
- ⚠️ 15-25 boxes per large sector (currently ~35-45 with minSize=35)
- ✅ 100% industry boxes transparent until hover
- ✅ 0px padding offsets (stock boxes fill entire area)
### Qualitative
- ✅ Visual appearance matches reference image 95%
- ✅ User can quickly identify major stocks at a glance
- ✅ Hover interactions feel natural and responsive
- ⚠️ Box density slightly higher than reference (needs minSize=50)
---
## 16. Recommended Next Steps
1. **Increase minSize to 50px** (Line 192 of SP500Page.tsx)
2. **Test visual appearance** with new threshold
3. **Verify all sectors** look balanced (especially small sectors)
4. **Deploy to production** after visual verification
5. **Monitor user feedback** for any complaints about missing stocks
---
## Contact & Questions
For design clarifications or implementation questions, refer to:
- **Project Documentation**: `if_invest/CLAUDE.md`
- **Design References**: `AI요청/snp500이미지.png` (reference), `AI요청/현재화면2.png` (current)
- **Codebase**: `if_invest/src/components/sp500/`
---
**Document Status**: Ready for Implementation
**Last Updated**: 2025-10-11
**Next Review**: After minSize optimization deployment

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
AI요청/현재화면1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 KiB

BIN
AI요청/현재화면2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

View File

@ -306,22 +306,32 @@ This is a multi-component application with the following structure:
- Components using this: `InvestmentSidebar.tsx`, `CompactAssetSelector.tsx`
- **S&P 500 Sector Visualization Page** (`/sp500` - 2025-10):
- **Layout System**: Fixed coordinate-based sector box positioning (not treemap algorithm)
- **Canvas Size**: 1350px × 900px (1.5× scale of base 900×600 grid)
- **Canvas Size**: 1880px × 1020px absolute pixel coordinates
- **Sector Boxes**: 11 manually positioned rectangular boxes for GICS sectors
- **Coordinate System**: Each sector defined by (x1, y1, x2, y2) pixel coordinates
- **Responsive Sizing**: Small sectors (8-11) use reduced font sizes and icon-only buttons
- **GICS Sectors** (official classification from DB):
1. TECHNOLOGY (82 stocks, 11 industries)
2. INDUSTRIALS (71 stocks, 19 industries)
3. FINANCIAL (69 stocks, 11 industries)
4. HEALTHCARE (60 stocks, 9 industries)
5. CONSUMER CYCLICAL (55 stocks, 19 industries)
6. CONSUMER DEFENSIVE (37 stocks, 11 industries)
7. UTILITIES (31 stocks, 6 industries)
8. REAL ESTATE (31 stocks, 9 industries)
9. COMMUNICATION SERVICES (25 stocks, 5 industries)
10. ENERGY (22 stocks, 5 industries)
11. BASIC MATERIALS (20 stocks, 7 industries)
- **CRITICAL - GICS Sector Naming** (2025-10-11):
- **MUST USE OFFICIAL GICS NAMES** from database exactly as stored
- Sector names in code MUST match `invest_product_code.sector` column values
- **Correct GICS Sector Names** (11 official sectors):
1. `"Information Technology"` (NOT "TECHNOLOGY")
2. `"Financials"` (NOT "FINANCIAL")
3. `"Consumer Discretionary"` (NOT "CONSUMER CYCLICAL")
4. `"Health Care"` (NOT "HEALTHCARE" - note the space)
5. `"Industrials"` (correct)
6. `"Consumer Staples"` (NOT "CONSUMER DEFENSIVE")
7. `"Communication Services"` (correct)
8. `"Energy"` (correct)
9. `"Utilities"` (correct)
10. `"Real Estate"` (correct - note the space)
11. `"Materials"` (NOT "BASIC MATERIALS")
- **Where to Update**:
- `SECTOR_BOXES` array (SP500Page.tsx lines 40-59)
- Technology sector conditional: `box.name === "Information Technology"` (line 948)
- Non-technology conditional: `box.name !== "Information Technology"` (line 1582)
- Modal dropdown options (lines 814-824)
- CSS data-sector attributes (SP500Page.css lines 444-508)
- **Impact**: Incorrect names cause data filtering failure (0 stocks displayed)
- **Detail Button**: Text "상세보기" for large sectors, magnifying glass icon for small sectors
- **Content Areas**: Each sector has header (title + detail button) and content area for stock treemap display
- **Design Pattern**: Absolute positioning with inline styles for exact pixel placement
@ -330,7 +340,7 @@ This is a multi-component application with the following structure:
- **Database as Source of Truth**:
- **CRITICAL**: Always use DB column values exactly as stored (`invest_product_code.sector` and `invest_product_code.industry`)
- DB has 503 S&P 500 stocks with `is_sp500='Y'` flag
- All 11 GICS sectors and 117 industries are correctly classified in database
- All 11 GICS sectors and 163 sub-industries are correctly classified in database
- Frontend Industry mapping MUST match DB values precisely (case-sensitive, hyphen-sensitive)
- Example correct mappings:
- "SOFTWARE-INFRASTRUCTURE" (16 stocks: MSFT, ORCL, PLTR, PANW, CRWD, SNPS, FTNT, XYZ, etc.)
@ -346,6 +356,14 @@ This is a multi-component application with the following structure:
- Other industries: Squarified treemap algorithm (`calculateTreemapLayout()`)
- Stock boxes colored by price change: green (>+1%), gray (0~+1%), dark red (0~-1%), red (<-1%)
- Dynamic font sizing based on box dimensions
- **Aggregated Stock Boxes** (2025-10-11):
- Small stocks (< 50px minSize) aggregated into single box per industry
- Positioned at bottom-right corner: `position: absolute; right: 4px; bottom: 4px`
- Market-cap weighted average: `Σ(changePercent × marketCap) / Σ(marketCap)`
- Dynamic sizing: 40-60px based on filtered stock count
- Color-coded using same 7-tier gradient as regular stocks
- Display: `+N` count with tooltip showing all filtered stocks
- Hover tooltip shows complete list of aggregated stocks sorted by weight
- **Date Comparison System**:
- Two-date selector: "비교 날짜" (current) vs "기준 날짜" (previous)
- Calendar UI with month navigation

View File

@ -0,0 +1,284 @@
# S&P 500 SECTOR VERIFICATION REPORT
**Database**: if_invest @ 3.38.180.110:8088
**Date**: 2025-10-11
**Total S&P 500 Stocks**: 503
---
## CRITICAL FINDING: SECTOR NAME MISMATCH
### Current Database Values (WRONG - Yahoo Finance Categories)
The database currently contains **YAHOO FINANCE SECTOR CATEGORIES**, not official GICS sectors:
1. `BASIC MATERIALS` (20 stocks) - Should be **"Materials"**
2. `COMMUNICATION SERVICES` (25 stocks) - Correct GICS name
3. `CONSUMER CYCLICAL` (55 stocks) - Should be **"Consumer Discretionary"**
4. `CONSUMER DEFENSIVE` (37 stocks) - Should be **"Consumer Staples"**
5. `ENERGY` (22 stocks) - Correct GICS name
6. `FINANCIAL` (69 stocks) - Should be **"Financials"** (with 's')
7. `HEALTHCARE` (60 stocks) - Should be **"Health Care"** (two words)
8. `INDUSTRIALS` (71 stocks) - Correct GICS name
9. `REAL ESTATE` (31 stocks) - Correct GICS name
10. `TECHNOLOGY` (82 stocks) - Should be **"Information Technology"**
11. `UTILITIES` (31 stocks) - Correct GICS name
### Expected GICS Sector Names (11 Official Sectors)
According to CLAUDE.md and Wikipedia GICS standard:
1. **Information Technology** (currently "TECHNOLOGY")
2. **Health Care** (currently "HEALTHCARE" - one word)
3. **Financials** (currently "FINANCIAL" - missing 's')
4. **Consumer Discretionary** (currently "CONSUMER CYCLICAL")
5. **Industrials** ✓ (correct)
6. **Consumer Staples** (currently "CONSUMER DEFENSIVE")
7. **Communication Services** ✓ (correct)
8. **Energy** ✓ (correct)
9. **Utilities** ✓ (correct)
10. **Real Estate** ✓ (correct)
11. **Materials** (currently "BASIC MATERIALS")
---
## Stock Count Per Sector (Current Database Values)
| Sector Name | Stock Count |
|-------------|-------------|
| TECHNOLOGY | 82 |
| INDUSTRIALS | 71 |
| FINANCIAL | 69 |
| HEALTHCARE | 60 |
| CONSUMER CYCLICAL | 55 |
| CONSUMER DEFENSIVE | 37 |
| UTILITIES | 31 |
| REAL ESTATE | 31 |
| COMMUNICATION SERVICES | 25 |
| ENERGY | 22 |
| BASIC MATERIALS | 20 |
| **TOTAL** | **503** |
---
## Industry Breakdown (Current Database - Yahoo Finance Categories)
### BASIC MATERIALS (20 stocks)
- SPECIALTY CHEMICALS: 10
- AGRICULTURAL INPUTS: 3
- BUILDING MATERIALS: 2
- STEEL: 2
- COPPER: 1
- GOLD: 1
- CHEMICALS: 1
### COMMUNICATION SERVICES (25 stocks)
- ENTERTAINMENT: 10
- TELECOM SERVICES: 5
- ADVERTISING AGENCIES: 4
- INTERNET CONTENT & INFORMATION: 4
- ELECTRONIC GAMING & MULTIMEDIA: 2
### CONSUMER CYCLICAL (55 stocks)
- TRAVEL SERVICES: 6
- PACKAGING & CONTAINERS: 6
- RESTAURANTS: 6
- AUTO PARTS: 5
- SPECIALTY RETAIL: 4
- RESIDENTIAL CONSTRUCTION: 4
- INTERNET RETAIL: 3
- AUTO MANUFACTURERS: 3
- APPAREL RETAIL: 3
- RESORTS & CASINOS: 3
- HOME IMPROVEMENT RETAIL: 2
- LODGING: 2
- FOOTWEAR & ACCESSORIES: 2
- (and 6 more sub-industries)
### CONSUMER DEFENSIVE (37 stocks)
- PACKAGED FOODS: 9
- HOUSEHOLD & PERSONAL PRODUCTS: 7
- DISCOUNT STORES: 5
- BEVERAGES-NON-ALCOHOLIC: 4
- FARM PRODUCTS: 3
- CONFECTIONERS: 2
- BEVERAGES-BREWERS: 2
- TOBACCO: 2
- (and 3 more sub-industries)
### ENERGY (22 stocks)
- OIL & GAS E&P: 10
- OIL & GAS MIDSTREAM: 4
- OIL & GAS EQUIPMENT & SERVICES: 3
- OIL & GAS REFINING & MARKETING: 3
- OIL & GAS INTEGRATED: 2
### FINANCIAL (69 stocks)
- ASSET MANAGEMENT: 12
- FINANCIAL DATA & STOCK EXCHANGES: 9
- BANKS-REGIONAL: 9
- INSURANCE-PROPERTY & CASUALTY: 9
- CREDIT SERVICES: 6
- INSURANCE BROKERS: 6
- CAPITAL MARKETS: 5
- BANKS-DIVERSIFIED: 5
- INSURANCE-LIFE: 4
- INSURANCE-DIVERSIFIED: 3
- INSURANCE-REINSURANCE: 1
### HEALTHCARE (60 stocks)
- DIAGNOSTICS & RESEARCH: 11
- MEDICAL DEVICES: 10
- MEDICAL INSTRUMENTS & SUPPLIES: 9
- DRUG MANUFACTURERS-GENERAL: 9
- HEALTHCARE PLANS: 7
- BIOTECHNOLOGY: 5
- MEDICAL DISTRIBUTION: 4
- MEDICAL CARE FACILITIES: 3
- DRUG MANUFACTURERS-SPECIALTY & GENERIC: 2
### INDUSTRIALS (71 stocks)
- SPECIALTY INDUSTRIAL MACHINERY: 17
- AEROSPACE & DEFENSE: 12
- BUILDING PRODUCTS & EQUIPMENT: 6
- INTEGRATED FREIGHT & LOGISTICS: 5
- RAILROADS: 4
- INDUSTRIAL DISTRIBUTION: 3
- AIRLINES: 3
- ENGINEERING & CONSTRUCTION: 3
- FARM & HEAVY CONSTRUCTION MACHINERY: 3
- (and 10 more sub-industries)
### REAL ESTATE (31 stocks)
- REIT-RESIDENTIAL: 7
- REIT-SPECIALTY: 7
- REIT-RETAIL: 5
- REIT-INDUSTRIAL: 3
- REIT-HEALTHCARE FACILITIES: 3
- REIT-OFFICE: 2
- REAL ESTATE SERVICES: 2
- REIT-HOTEL & MOTEL: 1
- REIT-DIVERSIFIED: 1
### TECHNOLOGY (82 stocks)
- SOFTWARE-APPLICATION: 17
- SOFTWARE-INFRASTRUCTURE: 16
- SEMICONDUCTORS: 13
- INFORMATION TECHNOLOGY SERVICES: 11
- COMPUTER HARDWARE: 6
- SCIENTIFIC & TECHNICAL INSTRUMENTS: 5
- ELECTRONIC COMPONENTS: 4
- SEMICONDUCTOR EQUIPMENT & MATERIALS: 4
- COMMUNICATION EQUIPMENT: 4
- SOLAR: 1
- CONSUMER ELECTRONICS: 1
### UTILITIES (31 stocks)
- UTILITIES - REGULATED ELECTRIC: 23
- UTILITIES - INDEPENDENT POWER PRODUCERS: 2
- UTILITIES - REGULATED GAS: 2
- UTILITIES - DIVERSIFIED: 2
- UTILITIES - RENEWABLE: 1
- UTILITIES - REGULATED WATER: 1
---
## Data Quality Status
- **NULL/Empty Sectors**: 0 stocks
- **NULL/Empty Industries**: 0 stocks
- **Total Coverage**: 100% (503/503 stocks have sector + industry data)
---
## ROOT CAUSE ANALYSIS
### Why Database Has Wrong Sector Names
The database contains **Yahoo Finance sector categories** instead of official GICS sectors because:
1. **Data Source**: The collection script uses `yfinance` library which returns Yahoo Finance's proprietary sector classification
2. **Yahoo Finance Mapping**: YF uses its own 11-sector system that differs from GICS:
- YF "Technology" → GICS "Information Technology"
- YF "Healthcare" → GICS "Health Care" (space difference)
- YF "Financial" → GICS "Financials" (plural)
- YF "Consumer Cyclical" → GICS "Consumer Discretionary"
- YF "Consumer Defensive" → GICS "Consumer Staples"
- YF "Basic Materials" → GICS "Materials"
3. **Previous Update Script**: The `update_gics_from_wikipedia.py` mentioned in CLAUDE.md (2025-10-07) was supposed to fix this, but either:
- Never ran successfully
- Was overwritten by subsequent yfinance-based updates
- Script exists but wasn't executed
---
## REQUIRED ACTION
### Immediate Fix Required
The frontend `SP500Page.tsx` is using **correct GICS sector names** (Information Technology, Health Care, Financials, etc.) but the database has **Yahoo Finance names** (TECHNOLOGY, HEALTHCARE, FINANCIAL, etc.).
This causes **ZERO STOCKS TO DISPLAY** because sector filtering fails to match.
### Solution Options
**Option 1: Update Database to GICS Names** (Recommended)
- Run SQL UPDATE to convert Yahoo Finance names to GICS names
- Preserve industry classifications (can keep Yahoo Finance industry names)
- Update `sector` column only for 6 mismatched sectors
**Option 2: Update Frontend to Yahoo Finance Names**
- Modify `SECTOR_BOXES` array in SP500Page.tsx
- Use "TECHNOLOGY" instead of "Information Technology"
- Use "HEALTHCARE" instead of "Health Care"
- Use "FINANCIAL" instead of "Financials"
- Use "CONSUMER CYCLICAL" instead of "Consumer Discretionary"
- Use "CONSUMER DEFENSIVE" instead of "Consumer Staples"
- Use "BASIC MATERIALS" instead of "Materials"
**Recommendation**: Use Option 1 (update database) to match official GICS standard as documented in CLAUDE.md.
---
## SQL Fix Script (Option 1)
```sql
-- Update Yahoo Finance sector names to official GICS names
UPDATE invest_product_code
SET sector = 'Information Technology'
WHERE sector = 'TECHNOLOGY' AND is_sp500 = 'Y';
UPDATE invest_product_code
SET sector = 'Health Care'
WHERE sector = 'HEALTHCARE' AND is_sp500 = 'Y';
UPDATE invest_product_code
SET sector = 'Financials'
WHERE sector = 'FINANCIAL' AND is_sp500 = 'Y';
UPDATE invest_product_code
SET sector = 'Consumer Discretionary'
WHERE sector = 'CONSUMER CYCLICAL' AND is_sp500 = 'Y';
UPDATE invest_product_code
SET sector = 'Consumer Staples'
WHERE sector = 'CONSUMER DEFENSIVE' AND is_sp500 = 'Y';
UPDATE invest_product_code
SET sector = 'Materials'
WHERE sector = 'BASIC MATERIALS' AND is_sp500 = 'Y';
-- Verify changes
SELECT sector, COUNT(*) as stock_count
FROM invest_product_code
WHERE is_sp500 = 'Y'
GROUP BY sector
ORDER BY stock_count DESC;
```
This will affect 377 stocks across 6 sectors while leaving 5 correct sectors unchanged (Communication Services, Energy, Industrials, Real Estate, Utilities).
---
**End of Report**

View File

@ -0,0 +1,135 @@
/* 마우스를 따라다니는 심플한 툴팁 */
.industry-tooltip-following {
position: fixed; /* 뷰포트 기준 */
z-index: 10000;
width: 280px;
background: rgba(20, 20, 20, 0.98);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
overflow: hidden;
pointer-events: none; /* 마우스 이벤트 통과 */
transition: opacity 0.15s ease; /* 부드러운 나타남/사라짐 */
}
/* 헤더 */
.tooltip-following-header {
padding: 10px 12px;
background: rgba(74, 144, 226, 0.9);
color: white;
font-size: 13px;
font-weight: 600;
text-align: left;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
/* 컨텐츠 영역 */
.tooltip-following-content {
padding: 8px;
}
/* 주식 행 */
.tooltip-stock-row {
display: grid;
grid-template-columns: 80px 80px 70px; /* 티커 | 주가 | 등락폭 */
gap: 8px;
padding: 6px 8px;
align-items: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
transition: background 0.15s ease;
}
.tooltip-stock-row:hover {
background: rgba(255, 255, 255, 0.05);
}
.tooltip-stock-row:last-child {
border-bottom: none;
}
/* 티커 */
.tooltip-ticker {
font-size: 12px;
font-weight: 700;
color: #FFFFFF;
text-align: left;
}
/* 주가 */
.tooltip-price {
font-size: 11px;
font-weight: 600;
color: #E0E0E0;
text-align: right;
}
/* 등락폭 */
.tooltip-change {
font-size: 11px;
font-weight: 600;
text-align: right;
}
/* More stocks 표시 */
.tooltip-more {
padding: 8px;
text-align: center;
font-size: 11px;
color: #999;
font-style: italic;
}
/* 스크롤바 스타일 */
.tooltip-following-content::-webkit-scrollbar {
width: 4px;
}
.tooltip-following-content::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
}
.tooltip-following-content::-webkit-scrollbar-thumb {
background: rgba(74, 144, 226, 0.6);
border-radius: 2px;
}
.tooltip-following-content::-webkit-scrollbar-thumb:hover {
background: rgba(74, 144, 226, 0.8);
}
/* 라이트 모드 */
html:not([data-theme="dark"]) .industry-tooltip-following {
background: rgba(255, 255, 255, 0.98);
border: 1px solid rgba(0, 0, 0, 0.15);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
}
html:not([data-theme="dark"]) .tooltip-following-header {
background: rgba(74, 144, 226, 0.9);
color: white;
}
html:not([data-theme="dark"]) .tooltip-stock-row {
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
html:not([data-theme="dark"]) .tooltip-stock-row:hover {
background: rgba(0, 0, 0, 0.03);
}
html:not([data-theme="dark"]) .tooltip-ticker {
color: #1E1E1E;
}
html:not([data-theme="dark"]) .tooltip-price {
color: #333333;
}
html:not([data-theme="dark"]) .tooltip-more {
color: #666;
}
html:not([data-theme="dark"]) .tooltip-following-content::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.05);
}

View File

@ -0,0 +1,72 @@
import React from "react";
import "./IndustryHoverTooltip.css";
export interface TooltipStock {
ticker: string;
companyName: string;
basePrice: number;
currentPrice: number;
changePercent: number;
weight: number;
}
export interface IndustryTooltipData {
sector: string;
industry: string;
stocks: TooltipStock[];
}
interface IndustryHoverTooltipProps {
data: IndustryTooltipData;
position: { x: number; y: number };
visible: boolean;
}
// Color coding helper
const getChangeColor = (changePercent: number): string => {
if (changePercent > 0) return "#00D084"; // 초록
if (changePercent < 0) return "#FF4757"; // 빨강
return "#B0B0B0"; // 회색
};
export const IndustryHoverTooltip: React.FC<IndustryHoverTooltipProps> = React.memo(
({ data, position, visible }) => {
if (!visible || !data) return null;
const { industry, stocks } = data;
return (
<div
className="industry-tooltip-following"
style={{
left: `${position.x + 15}px`, // 마우스 오른쪽 15px
top: `${position.y + 15}px`, // 마우스 아래 15px
}}
>
{/* 헤더: Industry 이름 */}
<div className="tooltip-following-header">
{industry}
</div>
{/* 주식 리스트 */}
<div className="tooltip-following-content">
{stocks.map((stock) => (
<div key={stock.ticker} className="tooltip-stock-row">
<div className="tooltip-ticker">{stock.ticker}</div>
<div className="tooltip-price">${stock.currentPrice.toFixed(2)}</div>
<div
className="tooltip-change"
style={{ color: getChangeColor(stock.changePercent) }}
>
{stock.changePercent > 0 ? "+" : ""}
{stock.changePercent.toFixed(2)}%
</div>
</div>
))}
</div>
</div>
);
}
);
IndustryHoverTooltip.displayName = "IndustryHoverTooltip";

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -97,6 +97,7 @@ export interface SP500StockData {
sector: string; // 섹터명 (예: "Technology")
sectorCode: string; // 섹터 코드 (예: "IT")
industry: string; // 산업 분류
companyName: string; // 회사명 (예: "Apple Inc.")
}
export interface SP500HeatmapMetadata {

View File

@ -1,60 +1,71 @@
.date-selector-container {
display: flex;
align-items: center;
gap: 2rem;
padding: 1.5rem 2rem;
background: var(--bg-card);
border-radius: 12px;
box-shadow: var(--shadow-sm);
margin-bottom: 2rem;
gap: 2.5rem;
padding: 2rem 2.5rem;
background: var(--gradient-card);
border-radius: 16px;
box-shadow: var(--shadow-lg);
border: 1px solid var(--border-primary);
backdrop-filter: blur(10px);
margin-bottom: 0;
max-width: 1350px;
margin-left: auto;
margin-right: auto;
flex: 1;
}
.date-selector-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
gap: 0.75rem;
flex: 1;
position: relative;
}
.date-selector-group label {
font-size: 0.9rem;
font-weight: 600;
color: var(--text-secondary);
font-size: 0.875rem;
font-weight: 700;
color: var(--brand-accent);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.date-select-button {
padding: 0.75rem 1rem;
border: 2px solid var(--border-primary);
border-radius: 8px;
background: var(--bg-primary);
padding: 1rem 1.25rem;
border: 2px solid transparent;
border-radius: 12px;
background: var(--bg-card);
color: var(--text-primary);
font-size: 1rem;
font-weight: 500;
font-size: 1.125rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
text-align: left;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
text-align: center;
box-shadow: var(--shadow-sm);
font-family: 'Courier New', monospace;
}
.date-select-button:hover {
border-color: #0ea5e9;
border-color: var(--brand-primary);
box-shadow: var(--shadow-md);
transform: translateY(-1px);
background: var(--bg-primary);
}
.date-select-button:focus {
outline: none;
border-color: #0ea5e9;
box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.1);
border-color: var(--brand-primary);
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.15);
}
.date-selector-divider {
font-size: 1.2rem;
font-weight: bold;
color: var(--text-secondary);
margin-top: 1.5rem;
font-size: 1.5rem;
font-weight: 900;
color: var(--brand-accent);
margin-top: 2rem;
flex-shrink: 0;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* 달력 팝업 */
@ -62,43 +73,62 @@
position: absolute;
top: 100%;
left: 0;
margin-top: 0.5rem;
background: var(--bg-card);
margin-top: 0.75rem;
background: var(--gradient-card);
border: 2px solid var(--border-primary);
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
padding: 1rem;
border-radius: 16px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.25);
backdrop-filter: blur(10px);
padding: 1.5rem;
z-index: 1000;
min-width: 320px;
min-width: 340px;
animation: slideDown 0.2s ease;
}
@keyframes slideDown {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.calendar-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid var(--border-primary);
margin-bottom: 1.25rem;
padding-bottom: 1rem;
border-bottom: 2px solid var(--border-primary);
}
.calendar-month {
font-size: 1rem;
font-weight: 700;
font-size: 1.125rem;
font-weight: 800;
color: var(--text-primary);
font-family: 'Courier New', monospace;
}
.calendar-nav-button {
background: none;
border: none;
color: var(--text-secondary);
font-size: 1.2rem;
background: var(--bg-card);
border: 1px solid var(--border-primary);
color: var(--text-primary);
font-size: 1.125rem;
cursor: pointer;
padding: 0.25rem 0.5rem;
transition: color 0.2s ease;
padding: 0.5rem 0.75rem;
border-radius: 8px;
transition: all 0.2s ease;
font-weight: 700;
}
.calendar-nav-button:hover {
color: #0ea5e9;
color: var(--brand-primary);
border-color: var(--brand-primary);
box-shadow: var(--shadow-sm);
transform: scale(1.05);
}
.calendar-grid {
@ -117,21 +147,22 @@
.calendar-weekdays > div {
text-align: center;
font-size: 0.75rem;
font-weight: 600;
color: var(--text-secondary);
padding: 0.25rem;
font-weight: 700;
color: var(--brand-accent);
padding: 0.5rem;
text-transform: uppercase;
}
.calendar-days {
display: flex;
flex-direction: column;
gap: 0.25rem;
gap: 0.375rem;
}
.calendar-week {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 0.25rem;
gap: 0.375rem;
}
.calendar-day {
@ -141,10 +172,11 @@
align-items: center;
justify-content: center;
position: relative;
border-radius: 6px;
font-size: 0.85rem;
border-radius: 10px;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.2s ease;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
font-weight: 600;
}
.calendar-day.empty {
@ -154,51 +186,181 @@
.calendar-day.unavailable {
color: var(--text-tertiary);
cursor: not-allowed;
opacity: 0.3;
opacity: 0.25;
}
.calendar-day.available {
color: var(--text-primary);
background: var(--bg-primary);
border: 1px solid var(--border-primary);
background: var(--bg-card);
border: 2px solid var(--border-primary);
box-shadow: var(--shadow-sm);
}
.calendar-day.available:hover {
background: #0ea5e9;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
transform: scale(1.05);
border-color: transparent;
transform: scale(1.1);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
z-index: 1;
}
.calendar-day.selected {
background: #0ea5e9;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-color: #0ea5e9;
font-weight: 700;
border-color: transparent;
font-weight: 800;
box-shadow: 0 6px 16px rgba(102, 126, 234, 0.5);
transform: scale(1.05);
}
.day-number {
font-size: 0.85rem;
font-size: 0.9rem;
font-family: 'Courier New', monospace;
}
/* 다크모드 */
[data-theme="dark"] .date-select-button {
background: #1F2937;
border-color: #374151;
[data-theme="dark"] .date-selector-container {
background: linear-gradient(135deg, rgba(31, 41, 55, 0.95) 0%, rgba(17, 24, 39, 0.95) 100%);
border-color: rgba(107, 114, 128, 0.3);
}
[data-theme="dark"] .date-select-button {
background: rgba(31, 41, 55, 0.8);
border-color: rgba(75, 85, 99, 0.5);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
[data-theme="dark"] .date-select-button:hover {
background: rgba(17, 24, 39, 0.9);
border-color: var(--brand-primary);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
[data-theme="dark"] .date-select-button:hover,
[data-theme="dark"] .date-select-button:focus {
border-color: #0ea5e9;
border-color: var(--brand-primary);
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.2);
}
[data-theme="dark"] .calendar-popup {
background: #1F2937;
border-color: #374151;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
background: linear-gradient(135deg, rgba(31, 41, 55, 0.98) 0%, rgba(17, 24, 39, 0.98) 100%);
border-color: rgba(75, 85, 99, 0.5);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.6);
}
[data-theme="dark"] .calendar-nav-button {
background: rgba(31, 41, 55, 0.8);
border-color: rgba(75, 85, 99, 0.5);
}
[data-theme="dark"] .calendar-nav-button:hover {
background: rgba(17, 24, 39, 0.9);
border-color: var(--brand-primary);
}
[data-theme="dark"] .calendar-day.available {
background: #111827;
border-color: #374151;
background: rgba(17, 24, 39, 0.8);
border-color: rgba(75, 85, 99, 0.5);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
}
[data-theme="dark"] .calendar-day.available:hover {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-color: transparent;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.5);
}
/* 모바일 반응형 */
@media (max-width: 768px) {
.date-selector-container {
flex-direction: column;
gap: 1.5rem;
padding: 1.5rem;
}
.date-selector-divider {
margin-top: 0;
transform: rotate(90deg);
}
.date-select-button {
font-size: 1rem;
padding: 0.875rem 1rem;
}
.calendar-popup {
min-width: 300px;
padding: 1.25rem;
}
.calendar-month {
font-size: 1rem;
}
.calendar-nav-button {
padding: 0.375rem 0.625rem;
font-size: 1rem;
}
.calendar-day {
font-size: 0.8rem;
}
.day-number {
font-size: 0.8rem;
}
}
@media (max-width: 480px) {
.date-selector-container {
padding: 1rem;
gap: 1rem;
}
.date-selector-group label {
font-size: 0.75rem;
}
.date-select-button {
font-size: 0.9rem;
padding: 0.75rem;
}
.date-selector-divider {
font-size: 1.25rem;
}
.calendar-popup {
min-width: 280px;
padding: 1rem;
}
.calendar-header {
margin-bottom: 1rem;
padding-bottom: 0.75rem;
}
.calendar-month {
font-size: 0.9rem;
}
.calendar-nav-button {
padding: 0.25rem 0.5rem;
font-size: 0.9rem;
}
.calendar-weekdays > div {
font-size: 0.65rem;
padding: 0.375rem;
}
.calendar-day {
font-size: 0.75rem;
border-radius: 8px;
}
.day-number {
font-size: 0.75rem;
}
}

View File

@ -5,6 +5,21 @@
position: relative;
}
/* Sector Color System */
:root {
--sector-technology: #4A90E2;
--sector-financial: #50C878;
--sector-consumer-cyclical: #FF6B6B;
--sector-healthcare: #9B59B6;
--sector-industrials: #F39C12;
--sector-consumer-defensive: #1ABC9C;
--sector-utilities: #3498DB;
--sector-real-estate: #E67E22;
--sector-communication-services: #E91E63;
--sector-energy: #2ECC71;
--sector-basic-materials: #95A5A6;
}
/* 섹터 상세보기 오버레이 */
.sector-detail-overlay {
position: fixed;
@ -74,6 +89,39 @@
gap: 1rem;
}
.sector-filter-dropdown {
padding: 0.5rem 1rem;
border: 2px solid var(--border-primary);
border-radius: 6px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
min-width: 280px;
max-width: 400px;
text-transform: none;
}
.sector-filter-dropdown:hover {
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
transform: translateY(-1px);
}
.sector-filter-dropdown:focus {
outline: none;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2);
}
.sector-filter-dropdown option {
background: var(--bg-secondary);
color: var(--text-primary);
font-weight: 600;
text-transform: none;
}
.ticker-search {
padding: 0.5rem 1rem;
border: 2px solid var(--border-primary);
@ -232,6 +280,12 @@
font-weight: 600;
}
.sector-cell {
color: var(--text-primary);
font-size: 0.875rem;
font-weight: 600;
}
.industry-cell {
color: var(--text-secondary);
font-size: 0.875rem;
@ -241,36 +295,47 @@
.sp500-controls {
display: flex;
justify-content: space-between;
align-items: center;
align-items: flex-start;
margin-bottom: 2rem;
gap: 1rem;
gap: 1.5rem;
max-width: 1350px;
margin-left: auto;
margin-right: auto;
}
/* 검색 버튼 */
.sp500-search-button {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1.5rem;
background: #0ea5e9;
gap: 0.625rem;
padding: 1rem 1.75rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
border: none;
border-radius: 8px;
border-radius: 12px;
cursor: pointer;
font-size: 1rem;
font-weight: 600;
transition: all 0.2s;
font-weight: 700;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
white-space: nowrap;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.25);
flex-shrink: 0;
margin-top: 1.5rem;
}
.sp500-search-button:hover {
background: #0284c7;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(14, 165, 233, 0.3);
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
}
.sp500-search-button:active {
transform: translateY(0);
}
.sp500-search-button svg {
flex-shrink: 0;
width: 20px;
height: 20px;
}
/* 로딩 및 에러 상태 */
@ -325,61 +390,168 @@
transform: translateY(-1px);
}
/* 1350x900 캔버스 (1.5배 확대) */
/* 1880x1020 캔버스 (FHD 최적화, 레퍼런스 이미지 정확한 크기) */
.sp500-canvas {
position: relative;
width: 1350px;
height: 900px;
background: #000;
width: 1880px;
height: 1020px;
border: 2px solid var(--border-primary);
box-shadow: var(--shadow-lg);
margin: 0 auto;
}
/* 다크모드 캔버스 */
html[data-theme="dark"] .sp500-canvas {
background: #000;
border: 2px solid #333;
}
/* 라이트모드 캔버스 */
html:not([data-theme="dark"]) .sp500-canvas {
background: #f8f9fa;
border: 2px solid #d0d0d0;
}
/* 섹터 박스 */
.sector-box {
position: absolute;
border: 2px solid #444;
background: rgba(30, 30, 30, 0.8);
border-radius: 0;
transition: all 0.3s ease;
}
.sector-box:hover {
/* 다크모드 섹터 박스 */
html[data-theme="dark"] .sector-box {
border: 2px solid #444;
background: rgba(30, 30, 30, 0.8);
}
html[data-theme="dark"] .sector-box:hover {
background: rgba(50, 50, 50, 0.9);
border-color: #0ea5e9;
z-index: 10;
}
/* 섹터 헤더 */
/* 라이트모드 섹터 박스 */
html:not([data-theme="dark"]) .sector-box {
border: 2px solid #b0b0b0;
background: rgba(255, 255, 255, 0.75);
}
html:not([data-theme="dark"]) .sector-box:hover {
background: rgba(240, 245, 255, 0.95);
border-color: #0ea5e9;
z-index: 10;
}
/* Individual Sector Styling with Colors - Database Sector Names (Yahoo Finance) */
.sector-box[data-sector="TECHNOLOGY"] {
border-top: 4px solid var(--sector-technology);
background: linear-gradient(135deg, rgba(74, 144, 226, 0.15) 0%, rgba(74, 144, 226, 0.05) 100%);
box-shadow: 0 4px 12px rgba(74, 144, 226, 0.3);
}
.sector-box[data-sector="FINANCIAL"] {
border-top: 4px solid var(--sector-financial);
background: linear-gradient(135deg, rgba(80, 200, 120, 0.15) 0%, rgba(80, 200, 120, 0.05) 100%);
box-shadow: 0 4px 12px rgba(80, 200, 120, 0.3);
}
.sector-box[data-sector="CONSUMER CYCLICAL"] {
border-top: 4px solid var(--sector-consumer-cyclical);
background: linear-gradient(135deg, rgba(255, 107, 107, 0.15) 0%, rgba(255, 107, 107, 0.05) 100%);
box-shadow: 0 4px 12px rgba(255, 107, 107, 0.3);
}
.sector-box[data-sector="HEALTHCARE"] {
border-top: 4px solid var(--sector-healthcare);
background: linear-gradient(135deg, rgba(155, 89, 182, 0.15) 0%, rgba(155, 89, 182, 0.05) 100%);
box-shadow: 0 4px 12px rgba(155, 89, 182, 0.3);
}
.sector-box[data-sector="INDUSTRIALS"] {
border-top: 4px solid var(--sector-industrials);
background: linear-gradient(135deg, rgba(243, 156, 18, 0.15) 0%, rgba(243, 156, 18, 0.05) 100%);
box-shadow: 0 4px 12px rgba(243, 156, 18, 0.3);
}
.sector-box[data-sector="CONSUMER DEFENSIVE"] {
border-top: 4px solid var(--sector-consumer-defensive);
background: linear-gradient(135deg, rgba(26, 188, 156, 0.15) 0%, rgba(26, 188, 156, 0.05) 100%);
box-shadow: 0 4px 12px rgba(26, 188, 156, 0.3);
}
.sector-box[data-sector="UTILITIES"] {
border-top: 4px solid var(--sector-utilities);
background: linear-gradient(135deg, rgba(52, 152, 219, 0.15) 0%, rgba(52, 152, 219, 0.05) 100%);
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.3);
}
.sector-box[data-sector="REAL ESTATE"] {
border-top: 4px solid var(--sector-real-estate);
background: linear-gradient(135deg, rgba(230, 126, 34, 0.15) 0%, rgba(230, 126, 34, 0.05) 100%);
box-shadow: 0 4px 12px rgba(230, 126, 34, 0.3);
}
.sector-box[data-sector="COMMUNICATION SERVICES"] {
border-top: 4px solid var(--sector-communication-services);
background: linear-gradient(135deg, rgba(233, 30, 99, 0.15) 0%, rgba(233, 30, 99, 0.05) 100%);
box-shadow: 0 4px 12px rgba(233, 30, 99, 0.3);
}
.sector-box[data-sector="ENERGY"] {
border-top: 4px solid var(--sector-energy);
background: linear-gradient(135deg, rgba(46, 204, 113, 0.15) 0%, rgba(46, 204, 113, 0.05) 100%);
box-shadow: 0 4px 12px rgba(46, 204, 113, 0.3);
}
.sector-box[data-sector="BASIC MATERIALS"] {
border-top: 4px solid var(--sector-basic-materials);
background: linear-gradient(135deg, rgba(149, 165, 166, 0.15) 0%, rgba(149, 165, 166, 0.05) 100%);
box-shadow: 0 4px 12px rgba(149, 165, 166, 0.3);
}
/* 섹터 헤더 - 오버레이 방식, 공백 제거 (레퍼런스 이미지 일치) */
.sector-header {
position: absolute;
top: 0;
left: 0;
right: 0;
top: 6px; /* 상단에서 약간 떨어짐 */
left: 6px;
z-index: 100; /* 컨텐츠 위에 표시 */
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 12px;
gap: 12px;
justify-content: flex-start;
align-items: center;
padding: 0; /* 패딩 제거 */
gap: 4px;
pointer-events: auto; /* 버튼 클릭 가능 */
}
/* 작은 영역 헤더 */
.sector-header.small {
padding: 6px;
gap: 4px;
top: 4px;
left: 4px;
gap: 2px;
}
/* 섹터 이름 */
.sector-name {
color: #fff;
font-size: 16px;
font-weight: bold;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.9);
text-align: left;
line-height: 1.2;
flex: 1;
}
/* 다크모드 섹터 이름 */
html[data-theme="dark"] .sector-name {
color: #fff;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.9);
}
/* 라이트모드 섹터 이름 */
html:not([data-theme="dark"]) .sector-name {
color: #1a1a1a;
text-shadow: 1px 1px 2px rgba(255, 255, 255, 0.8);
}
/* 작은 영역 섹터 이름 */
.sector-name.small {
font-size: 10px;
@ -427,25 +599,20 @@
transform: translateY(0);
}
/* 섹터 컨텐츠 영역 (헤더 아래 꽉 채우기) */
/* 섹터 컨텐츠 영역 (헤더 바로 아래, 공백 없음) */
.sector-content {
position: absolute;
top: 40px; /* 헤더 높이 (padding 12px + 폰트 16px + 여백) */
left: 8px;
right: 8px;
bottom: 8px;
border: 2px solid rgba(100, 100, 100, 0.5);
border-radius: 4px;
background: rgba(20, 20, 20, 0.6);
top: 0; /* 헤더 없이 상단부터 시작 - 레퍼런스 이미지 일치 */
left: 0;
right: 0;
bottom: 0;
border-radius: 0; /* 섹터 박스 border-radius와 일치 */
border: none; /* 섹터 박스 테두리만 사용 */
background: transparent; /* 섹터 박스 배경 사용 */
}
/* 작은 영역의 컨텐츠 영역 */
.sector-header.small ~ .sector-content {
top: 24px; /* 작은 헤더 높이 (padding 6px + 폰트 8px + 여백) */
left: 4px;
right: 4px;
bottom: 4px;
}
/* 다크모드/라이트모드 섹터 컨텐츠는 섹터 박스 배경 사용 */
/* border와 background 제거 - 투명하게 유지 */
/* ===== 모바일 반응형 스타일 ===== */
@ -486,6 +653,12 @@
padding: 0.75rem 0.5rem;
}
.sector-filter-dropdown {
min-width: 250px;
max-width: 350px;
font-size: 0.875rem;
}
.ticker-search {
width: 150px;
font-size: 0.875rem;
@ -525,6 +698,12 @@
gap: 0.75rem;
}
.sector-filter-dropdown {
width: 100%;
font-size: 0.9rem;
min-width: auto;
}
.ticker-search {
width: 100%;
}

View File

@ -0,0 +1,283 @@
# S&P 500 Sector Name Mapping Reference
## Database Reality vs GICS Standard
**Source**: Database uses **Yahoo Finance sector classifications**, NOT official GICS sector names.
---
## Complete Mapping Table
| Database Value (ACTUAL) | GICS Official Name | Frontend Display | Notes |
|------------------------|-------------------|------------------|-------|
| `TECHNOLOGY` | Information Technology | Technology / IT | **Most critical** - affects 82 stocks |
| `FINANCIAL` | Financials | Financials / Finance | Affects 69 stocks |
| `HEALTHCARE` | Health Care | Health Care | Affects 60 stocks (no space in DB) |
| `CONSUMER CYCLICAL` | Consumer Discretionary | Consumer Cyclical | Affects 55 stocks |
| `CONSUMER DEFENSIVE` | Consumer Staples | Consumer Defensive | Affects 37 stocks |
| `BASIC MATERIALS` | Materials | Basic Materials | Affects 20 stocks (has space in DB) |
| `INDUSTRIALS` | Industrials | Industrials | Affects 71 stocks |
| `UTILITIES` | Utilities | Utilities | Affects 31 stocks |
| `REAL ESTATE` | Real Estate | Real Estate | Affects 31 stocks (has space) |
| `COMMUNICATION SERVICES` | Communication Services | Communication Services | Affects 25 stocks (matches GICS) |
| `ENERGY` | Energy | Energy | Affects 22 stocks (matches GICS) |
---
## Frontend Implementation Pattern
### Option 1: Use DB Names Directly
```typescript
// Use database values in code
const sectorName = "TECHNOLOGY";
// Create display mapping for UI
const SECTOR_DISPLAY_MAP: Record<string, string> = {
"TECHNOLOGY": "Information Technology",
"FINANCIAL": "Financials",
"HEALTHCARE": "Health Care",
"CONSUMER CYCLICAL": "Consumer Discretionary",
"CONSUMER DEFENSIVE": "Consumer Staples",
"BASIC MATERIALS": "Materials",
"INDUSTRIALS": "Industrials",
"UTILITIES": "Utilities",
"REAL ESTATE": "Real Estate",
"COMMUNICATION SERVICES": "Communication Services",
"ENERGY": "Energy"
};
// Display to user
const displayName = SECTOR_DISPLAY_MAP[sectorName];
```
### Option 2: Update Database (NOT RECOMMENDED)
Would require:
1. Update 503 rows in `invest_product_code` table
2. Update 377,570 rows in `stock_market_cap_history` table
3. Regenerate all S3 heatmap JSON files (753 files)
4. Risk data inconsistency
---
## Critical Code Locations in Frontend
### SP500Page.tsx
**1. SECTOR_BOXES Array (Lines 40-59)**
```typescript
// ❌ CURRENT (WRONG)
const SECTOR_BOXES = [
{ name: "Information Technology", ... }, // Returns 0 stocks
// ...
];
// ✅ CORRECT
const SECTOR_BOXES = [
{
name: "TECHNOLOGY", // DB value
displayName: "Information Technology", // For UI
// ...
},
];
```
**2. Technology Sector Conditional (Line 948)**
```typescript
// ❌ WRONG
box.name === "Information Technology"
// ✅ CORRECT
box.name === "TECHNOLOGY"
```
**3. Non-Technology Conditional (Line 1582)**
```typescript
// ❌ WRONG
box.name !== "Information Technology"
// ✅ CORRECT
box.name !== "TECHNOLOGY"
```
**4. Modal Dropdown (Lines 814-824)**
```typescript
// ❌ WRONG
<option value="Information Technology">Information Technology</option>
// ✅ CORRECT
<option value="TECHNOLOGY">Information Technology</option>
{/* Use DB value in value attribute, display name in label */}
```
### SP500Page.css (Lines 444-508)
**data-sector Attributes**
```css
/* ❌ WRONG */
[data-sector="Information Technology"] { ... }
/* ✅ CORRECT */
[data-sector="TECHNOLOGY"] { ... }
```
---
## SQL Query Pattern
### Correct Queries
```sql
-- Get all Technology stocks
SELECT * FROM invest_product_code
WHERE sector = 'TECHNOLOGY' AND is_sp500 = 'Y';
-- Get specific industry
SELECT * FROM invest_product_code
WHERE sector = 'TECHNOLOGY'
AND industry = 'SOFTWARE-INFRASTRUCTURE'
AND is_sp500 = 'Y';
```
### Industry Names (Also Case-Sensitive)
```sql
-- ❌ WRONG (0 results)
WHERE industry = 'Software-Infrastructure'
-- ✅ CORRECT
WHERE industry = 'SOFTWARE-INFRASTRUCTURE'
```
---
## Data Generation Scripts
### Backend Python Scripts Must Use DB Values
**generate_sp500_heatmap_json.py**
```python
# ✓ Already uses DB values correctly
sectors = df['sector'].unique() # Returns "TECHNOLOGY", not "Information Technology"
```
**Any new scripts must query with DB values**
```python
# ❌ WRONG
query = "SELECT * FROM invest_product_code WHERE sector = 'Information Technology'"
# ✅ CORRECT
query = "SELECT * FROM invest_product_code WHERE sector = 'TECHNOLOGY'"
```
---
## Testing Checklist
### Verify Each Sector Returns Data
```typescript
const sectors = [
"TECHNOLOGY", // Should return 82 stocks
"INDUSTRIALS", // Should return 71 stocks
"FINANCIAL", // Should return 69 stocks
"HEALTHCARE", // Should return 60 stocks
"CONSUMER CYCLICAL", // Should return 55 stocks
"CONSUMER DEFENSIVE", // Should return 37 stocks
"UTILITIES", // Should return 31 stocks
"REAL ESTATE", // Should return 31 stocks
"COMMUNICATION SERVICES", // Should return 25 stocks
"ENERGY", // Should return 22 stocks
"BASIC MATERIALS" // Should return 20 stocks
];
```
### Test Industry Filtering
```typescript
// Technology sector industries
const techIndustries = [
"SOFTWARE-APPLICATION", // 17 stocks
"SOFTWARE-INFRASTRUCTURE", // 16 stocks
"SEMICONDUCTORS", // 13 stocks
"INFORMATION TECHNOLOGY SERVICES",// 11 stocks
"COMPUTER HARDWARE", // 6 stocks
"SCIENTIFIC & TECHNICAL INSTRUMENTS", // 5 stocks
"ELECTRONIC COMPONENTS", // 4 stocks
"SEMICONDUCTOR EQUIPMENT & MATERIALS", // 4 stocks
"COMMUNICATION EQUIPMENT", // 4 stocks
"CONSUMER ELECTRONICS", // 1 stock (AAPL)
"SOLAR" // 1 stock (FSLR)
];
```
---
## Why This Happened
1. **Data Source**: `collect_sp500_market_cap_daily.py` uses `yfinance` library
2. **Yahoo Finance**: Uses abbreviated sector names, not GICS standard
3. **Database Design**: Stored raw Yahoo Finance sector values without normalization
4. **Documentation**: CLAUDE.md incorrectly stated GICS names were used
---
## Recommendation
**DO NOT change database values**. Instead:
1. ✅ Update frontend code to use database values (`TECHNOLOGY`, `FINANCIAL`, etc.)
2. ✅ Create display name mapping for user-facing text
3. ✅ Update CLAUDE.md to reflect actual database schema
4. ✅ Add this mapping reference to project documentation
This approach:
- Requires minimal code changes (frontend only)
- Preserves historical data integrity
- Avoids regenerating 753 S3 JSON files
- Maintains consistency with data collection scripts
---
## Updated CLAUDE.md Section
**Replace this section in CLAUDE.md**:
```markdown
## S&P 500 Sector Classification
**CRITICAL**: Database uses **Yahoo Finance sector names** (UPPERCASE), NOT official GICS names.
### Sector Names in Database (11 sectors)
1. `TECHNOLOGY` (82 stocks) - GICS: "Information Technology"
2. `INDUSTRIALS` (71 stocks) - GICS: "Industrials"
3. `FINANCIAL` (69 stocks) - GICS: "Financials"
4. `HEALTHCARE` (60 stocks) - GICS: "Health Care"
5. `CONSUMER CYCLICAL` (55 stocks) - GICS: "Consumer Discretionary"
6. `CONSUMER DEFENSIVE` (37 stocks) - GICS: "Consumer Staples"
7. `UTILITIES` (31 stocks) - GICS: "Utilities"
8. `REAL ESTATE` (31 stocks) - GICS: "Real Estate"
9. `COMMUNICATION SERVICES` (25 stocks) - GICS: "Communication Services"
10. `ENERGY` (22 stocks) - GICS: "Energy"
11. `BASIC MATERIALS` (20 stocks) - GICS: "Materials"
### Industry Classification
- Total: 112 unique industries across all sectors
- Format: UPPERCASE with hyphens (e.g., "SOFTWARE-APPLICATION")
- Technology sector: 11 industries
- See `SECTOR_NAME_MAPPING.md` for complete industry list
```
---
## Quick Reference Commands
### Check Sector Names
```bash
python -c "import psycopg2; conn = psycopg2.connect(host='3.38.180.110', port=8088, database='if_invest', user='eldsoft', password='eld240510'); cur = conn.cursor(); cur.execute('SELECT DISTINCT sector FROM invest_product_code WHERE is_sp500=\'Y\' ORDER BY sector'); print('\n'.join([row[0] for row in cur.fetchall()])); conn.close()"
```
### Count Stocks by Sector
```bash
python -c "import psycopg2; conn = psycopg2.connect(host='3.38.180.110', port=8088, database='if_invest', user='eldsoft', password='eld240510'); cur = conn.cursor(); cur.execute('SELECT sector, COUNT(*) FROM invest_product_code WHERE is_sp500=\'Y\' GROUP BY sector ORDER BY COUNT(*) DESC'); print('\n'.join([f'{row[0]:30s} : {row[1]:3d}' for row in cur.fetchall()])); conn.close()"
```
### List Technology Industries
```bash
python -c "import psycopg2; conn = psycopg2.connect(host='3.38.180.110', port=8088, database='if_invest', user='eldsoft', password='eld240510'); cur = conn.cursor(); cur.execute('SELECT industry, COUNT(*) FROM invest_product_code WHERE is_sp500=\'Y\' AND sector=\'TECHNOLOGY\' GROUP BY industry ORDER BY COUNT(*) DESC'); print('\n'.join([f'{row[0]:50s} : {row[1]:3d}' for row in cur.fetchall()])); conn.close()"
```

View File

@ -0,0 +1,340 @@
# S&P 500 Industry Subdivision Database Verification Report
**Date**: 2025-10-11
**Database**: 3.38.180.110:8088/if_invest
**Total S&P 500 Stocks**: 503
**Total Sectors**: 11
---
## CRITICAL FINDING: Sector Name Mismatch
### ⚠️ Database Reality vs CLAUDE.md Documentation
**CLAUDE.md states**:
- Sector should be "Information Technology" (with space)
- Referenced as official GICS sector name
**ACTUAL DATABASE**:
- Sector is stored as **"TECHNOLOGY"** (uppercase, no space)
- All 82 tech stocks use "TECHNOLOGY" as sector value
**Impact**: Frontend code using "Information Technology" will return ZERO stocks.
---
## Technology Sector Complete Breakdown
### Summary
- **Sector Name in DB**: `TECHNOLOGY` (NOT "Information Technology")
- **Total Industries**: 11
- **Total Stocks**: 82
### Industry Subdivision with Stock Counts
| Industry Name | Stock Count | Key Stocks |
|--------------|-------------|------------|
| **SOFTWARE-APPLICATION** | 17 | CRM, ADBE, INTU, NOW, WDAY, UBER |
| **SOFTWARE-INFRASTRUCTURE** | 16 | MSFT, ORCL, PLTR, PANW, CRWD, SNPS, FTNT |
| **SEMICONDUCTORS** | 13 | NVDA, AMD, AVGO, INTC, QCOM, MU |
| **INFORMATION TECHNOLOGY SERVICES** | 11 | ACN, IBM, FIS, CTSH, FI |
| **COMPUTER HARDWARE** | 6 | DELL, HPQ, SMCI, ANET, STX, WDC |
| **SCIENTIFIC & TECHNICAL INSTRUMENTS** | 5 | KEYS, GRMN, FTV, TDY, TRMB |
| **ELECTRONIC COMPONENTS** | 4 | APH, GLW, TEL, JBL |
| **SEMICONDUCTOR EQUIPMENT & MATERIALS** | 4 | AMAT, KLAC, LRCX, TER |
| **COMMUNICATION EQUIPMENT** | 4 | CSCO, HPE, MSI, ZBRA |
| **CONSUMER ELECTRONICS** | 1 | AAPL |
| **SOLAR** | 1 | FSLR |
---
## Complete Technology Sector Stock List
### 1. SOFTWARE-APPLICATION (17 stocks)
```
ADBE : Adobe Inc.
ADP : Automatic Data Processing, Inc.
ADSK : Autodesk
CDNS : Cadence Design Systems
CRM : Salesforce, Inc.
DAY : Dayforce
DDOG : Datadog
FICO : Fair Isaac
INTU : Intuit Inc.
NOW : ServiceNow, Inc.
PAYC : Paycom
PAYX : Paychex
PTC : PTC Inc.
ROP : Roper Technologies
TYL : Tyler Technologies
UBER : Uber Technologies, Inc.
WDAY : Workday, Inc.
```
### 2. SOFTWARE-INFRASTRUCTURE (16 stocks)
```
AKAM : Akamai Technologies
CPAY : Corpay
CRWD : CrowdStrike
FFIV : F5, Inc.
FTNT : Fortinet
GDDY : GoDaddy
GEN : Gen Digital
GPN : Global Payments
MSFT : Microsoft Corp.
NTAP : NetApp
ORCL : Oracle Corp.
PANW : Palo Alto Networks, Inc.
PLTR : Palantir Technologies Inc.
SNPS : Synopsys
VRSN : Verisign
XYZ : Block, Inc.
```
### 3. SEMICONDUCTORS (13 stocks)
```
ADI : Analog Devices, Inc.
AMD : Advanced Micro Devices, Inc.
AVGO : Broadcom Inc.
INTC : Intel Corp.
MCHP : Microchip Technology
MPWR : Monolithic Power Systems
MU : Micron Technology
NVDA : NVIDIA Corp.
NXPI : NXP Semiconductors
ON : ON Semiconductor
QCOM : Qualcomm Inc.
SWKS : Skyworks Solutions
TXN : Texas Instruments Incorporated
```
### 4. INFORMATION TECHNOLOGY SERVICES (11 stocks)
```
ACN : Accenture plc
BR : Broadridge Financial Solutions
CDW : CDW Corporation
CTSH : Cognizant
EPAM : EPAM Systems
FI : Fiserv
FIS : Fidelity National Information Services
IBM : International Business Machines Corporation
IT : Gartner
JKHY : Jack Henry & Associates
LDOS : Leidos
```
### 5. COMPUTER HARDWARE (6 stocks)
```
ANET : Arista Networks
DELL : Dell Technologies Inc.
HPQ : HP Inc.
SMCI : Supermicro
STX : Seagate Technology
WDC : Western Digital
```
### 6. SCIENTIFIC & TECHNICAL INSTRUMENTS (5 stocks)
```
FTV : Fortive
GRMN : Garmin
KEYS : Keysight Technologies
TDY : Teledyne Technologies
TRMB : Trimble Inc.
```
### 7. ELECTRONIC COMPONENTS (4 stocks)
```
APH : Amphenol
GLW : Corning Inc.
JBL : Jabil
TEL : TE Connectivity
```
### 8. SEMICONDUCTOR EQUIPMENT & MATERIALS (4 stocks)
```
AMAT : Applied Materials
KLAC : KLA Corporation
LRCX : Lam Research Corporation
TER : Teradyne
```
### 9. COMMUNICATION EQUIPMENT (4 stocks)
```
CSCO : Cisco Systems, Inc.
HPE : Hewlett Packard Enterprise
MSI : Motorola Solutions
ZBRA : Zebra Technologies
```
### 10. CONSUMER ELECTRONICS (1 stock)
```
AAPL : Apple Inc.
```
### 11. SOLAR (1 stock)
```
FSLR : First Solar
```
---
## All 11 Sectors Overview
| Sector Name (DB) | Industries | Stocks | Status |
|-----------------|-----------|---------|---------|
| **TECHNOLOGY** | 11 | 82 | ✓ Verified |
| **INDUSTRIALS** | 19 | 71 | ✓ Verified |
| **FINANCIAL** | 11 | 69 | ✓ Verified |
| **HEALTHCARE** | 9 | 60 | ✓ Verified |
| **CONSUMER CYCLICAL** | 19 | 55 | ✓ Verified |
| **CONSUMER DEFENSIVE** | 11 | 37 | ✓ Verified |
| **UTILITIES** | 6 | 31 | ✓ Verified |
| **REAL ESTATE** | 9 | 31 | ✓ Verified |
| **COMMUNICATION SERVICES** | 5 | 25 | ✓ Verified |
| **ENERGY** | 5 | 22 | ✓ Verified |
| **BASIC MATERIALS** | 7 | 20 | ✓ Verified |
**Total**: 112 unique industries across 503 stocks
---
## Database Sector Names (Exact Values)
**⚠️ CRITICAL**: Frontend code MUST use these EXACT sector names:
1. `TECHNOLOGY` (NOT "Information Technology")
2. `INDUSTRIALS` (NOT "Industrials")
3. `FINANCIAL` (NOT "Financials")
4. `HEALTHCARE` (NOT "Health Care")
5. `CONSUMER CYCLICAL` (NOT "Consumer Discretionary")
6. `CONSUMER DEFENSIVE` (NOT "Consumer Staples")
7. `UTILITIES` (correct)
8. `REAL ESTATE` (correct - has space)
9. `COMMUNICATION SERVICES` (correct - has space)
10. `ENERGY` (correct)
11. `BASIC MATERIALS` (NOT "Materials" - has space)
---
## Data Quality Check
### ✓ All Passed
- All 503 S&P 500 stocks have sector classification
- All 503 S&P 500 stocks have industry classification
- No NULL or empty values found
- All stocks have `is_sp500='Y'` flag
### Data Consistency
- All sector names are UPPERCASE
- All industry names are UPPERCASE
- Industry names use hyphens for compounds (e.g., "SOFTWARE-APPLICATION")
- Some industries use ampersands (e.g., "OIL & GAS E&P")
- Some industries use spaces (e.g., "UTILITIES - REGULATED ELECTRIC")
---
## Frontend Code Implications
### Current Issue in SP500Page.tsx
**Line 40-59 SECTOR_BOXES array**:
```typescript
// ❌ WRONG - Will return 0 stocks
{ name: "Information Technology", x1: 20, y1: 20, x2: 920, y2: 720 }
// ✅ CORRECT - Must use DB value
{ name: "TECHNOLOGY", x1: 20, y1: 20, x2: 920, y2: 720 }
```
**Line 948 & 1582 conditionals**:
```typescript
// ❌ WRONG
box.name === "Information Technology"
// ✅ CORRECT
box.name === "TECHNOLOGY"
```
**Lines 814-824 Modal dropdown**:
```typescript
// ❌ WRONG - Will show 0 stocks
<option value="Information Technology">Information Technology</option>
// ✅ CORRECT
<option value="TECHNOLOGY">Technology</option>
```
### Industry Name Mapping (All UPPERCASE)
Frontend MUST use exact industry strings from database:
```typescript
const TECH_INDUSTRIES = [
"SOFTWARE-APPLICATION", // 17 stocks
"SOFTWARE-INFRASTRUCTURE", // 16 stocks
"SEMICONDUCTORS", // 13 stocks
"INFORMATION TECHNOLOGY SERVICES",// 11 stocks
"COMPUTER HARDWARE", // 6 stocks
"SCIENTIFIC & TECHNICAL INSTRUMENTS", // 5 stocks
"ELECTRONIC COMPONENTS", // 4 stocks
"SEMICONDUCTOR EQUIPMENT & MATERIALS", // 4 stocks
"COMMUNICATION EQUIPMENT", // 4 stocks
"CONSUMER ELECTRONICS", // 1 stock (AAPL only)
"SOLAR" // 1 stock (FSLR only)
];
```
---
## Recommended Frontend Changes
### 1. Update SECTOR_BOXES Array
Change all sector names to match database values exactly.
### 2. Update Industry Filters
Use uppercase industry names with exact punctuation from database.
### 3. Update Display Names
Create a display name mapping for user-friendly names:
```typescript
const SECTOR_DISPLAY_NAMES: Record<string, string> = {
"TECHNOLOGY": "Information Technology",
"FINANCIAL": "Financials",
"HEALTHCARE": "Health Care",
"CONSUMER CYCLICAL": "Consumer Discretionary",
"CONSUMER DEFENSIVE": "Consumer Staples",
"BASIC MATERIALS": "Materials",
// ... etc
};
```
### 4. Data Filtering Pattern
```typescript
// Filter stocks by sector
const techStocks = heatmapData.data.filter(
stock => stock.sector === "TECHNOLOGY" // Use DB value, not display name
);
// Filter stocks by industry
const semiconductors = techStocks.filter(
stock => stock.industry === "SEMICONDUCTORS" // Exact match, case-sensitive
);
```
---
## Files Generated
1. `verify_sp500_industries.py` - Database verification script
2. `sp500_industries_verification.json` - Complete JSON export
3. `get_tech_stocks.py` - Technology sector detailed query
4. `SP500_INDUSTRY_VERIFICATION_REPORT.md` - This report
---
## Conclusion
The database uses **Yahoo Finance sector classifications** (UPPERCASE, abbreviated), NOT official GICS sector names as documented in CLAUDE.md. The frontend MUST be updated to use the actual database values for data retrieval to work correctly.
**Action Required**: Update frontend code to use database sector names ("TECHNOLOGY", "FINANCIAL", etc.) instead of GICS names ("Information Technology", "Financials", etc.).

View File

@ -0,0 +1,49 @@
"""
stock_market_cap_history 테이블의 sector와 industry 확인
"""
from db_conn import DBConn
def check_market_cap_sectors():
"""stock_market_cap_history 테이블의 sector와 industry 값 확인"""
conn = DBConn()
# 최근 데이터 샘플 조회
sql = """
SELECT invest_code, sector, industry, snapshot_date
FROM stock_market_cap_history
WHERE snapshot_date = (SELECT MAX(snapshot_date) FROM stock_market_cap_history)
ORDER BY invest_code
LIMIT 20
"""
records = conn.fetchall(sql)
conn.close()
print("=" * 80)
print("stock_market_cap_history 테이블 샘플 (최근 날짜)")
print("=" * 80)
for invest_code, sector, industry, snapshot_date in records:
print(f"{invest_code:10s} | {sector:30s} | {industry}")
print("\n" + "=" * 80)
# Sector 종류 확인
conn2 = DBConn()
sql2 = """
SELECT DISTINCT sector
FROM stock_market_cap_history
ORDER BY sector
"""
sectors = conn2.fetchall(sql2)
conn2.close()
print("\n전체 Sector 목록:")
print("-" * 80)
for (sector,) in sectors:
print(f" - {sector}")
if __name__ == "__main__":
check_market_cap_sectors()

View File

@ -0,0 +1,72 @@
"""
DB의 실제 sector와 industry 확인
"""
from db_conn import DBConn
def check_sectors_and_industries():
"""invest_product_code 테이블의 sector와 industry 값 확인"""
conn = DBConn()
# S&P 500 종목의 sector와 industry 조회
sql = """
SELECT DISTINCT sector, industry
FROM invest_product_code
WHERE is_sp500 = 'Y'
ORDER BY sector, industry
"""
records = conn.fetchall(sql)
conn.close()
# Sector별로 그룹핑
sector_industry_map = {}
for sector, industry in records:
if sector not in sector_industry_map:
sector_industry_map[sector] = []
if industry:
sector_industry_map[sector].append(industry)
# 결과 출력
print("=" * 80)
print("S&P 500 Sectors and Industries")
print("=" * 80)
for sector in sorted(sector_industry_map.keys()):
print(f"\n{sector}")
print("-" * 80)
industries = sorted(set(sector_industry_map[sector]))
for industry in industries:
print(f" - {industry}")
print("\n" + "=" * 80)
print(f"Total Sectors: {len(sector_industry_map)}")
print("=" * 80)
# Sector 코드 매핑 제안
print("\n" + "=" * 80)
print("Suggested Sector Code Mapping:")
print("=" * 80)
sector_code_suggestions = {
"Information Technology": "IT",
"Health Care": "HC",
"Financials": "FN",
"Consumer Discretionary": "CD",
"Industrials": "IN",
"Consumer Staples": "CS",
"Communication Services": "CP",
"Energy": "EN",
"Utilities": "UT",
"Real Estate": "RE",
"Materials": "MT"
}
for sector in sorted(sector_industry_map.keys()):
code = sector_code_suggestions.get(sector, "XX")
print(f'"{sector}": "{code}",')
if __name__ == "__main__":
check_sectors_and_industries()

View File

@ -45,32 +45,23 @@ def get_all_available_dates():
return dates
def get_sector_code(sector):
"""섹터명을 2자리 코드로 변환 (yfinance + GICS 혼용 지원)"""
"""섹터명을 2자리 코드로 변환 (yfinance 형식)"""
if not sector:
return "OT"
# yfinance 섹터명 + GICS 섹터명 혼용 매핑
# yfinance 섹터명 매핑 (DB 실제 값 기준)
sector_map = {
# yfinance 섹터명
"Technology": "IT",
"Healthcare": "HC",
"Financial Services": "FN",
"Consumer Cyclical": "CD",
"Industrials": "IN",
"Communication Services": "CS",
"Consumer Defensive": "CP",
"Energy": "EN",
"Utilities": "UT",
"Real Estate": "RE",
"Basic Materials": "MT",
# GICS 표준 섹터명 (호환성)
"Information Technology": "IT",
"Health Care": "HC",
"Financials": "FN",
"Consumer Discretionary": "CD",
"Consumer Staples": "CP",
"Materials": "MT"
"TECHNOLOGY": "IT",
"HEALTHCARE": "HC",
"FINANCIAL": "FN",
"CONSUMER CYCLICAL": "CD",
"INDUSTRIALS": "IN",
"COMMUNICATION SERVICES": "CS",
"CONSUMER DEFENSIVE": "CP",
"ENERGY": "EN",
"UTILITIES": "UT",
"REAL ESTATE": "RE",
"BASIC MATERIALS": "MT",
}
return sector_map.get(sector, "OT") # OT = Other
@ -85,7 +76,7 @@ def generate_heatmap_data(target_date):
conn = DBConn()
# S&P 500 시가총액 + 캔들 종가 + 섹터/산업 조인 쿼리
# S&P 500 시가총액 + 캔들 종가 + 섹터/산업 + 회사명 조인 쿼리
sql = """
SELECT
h.invest_code,
@ -93,12 +84,15 @@ def generate_heatmap_data(target_date):
c.close,
h.market_cap_usd,
h.sector,
h.industry
h.industry,
p.code_desc_en
FROM stock_market_cap_history h
INNER JOIN invest_candles c
ON h.invest_code = c.invest_code
AND h.snapshot_date = c.target_dt
AND c.interval = '1d'
INNER JOIN invest_product_code p
ON h.invest_code = p.invest_code
WHERE h.snapshot_date = %s
AND h.sp500_weight_percent IS NOT NULL
ORDER BY h.sp500_weight_percent DESC
@ -115,9 +109,10 @@ def generate_heatmap_data(target_date):
total_market_cap = 0
sectors_set = set()
for ticker, weight, close_price, market_cap, sector, industry in records:
for ticker, weight, close_price, market_cap, sector, industry, company_name in records:
sector_name = sector or "Unknown"
industry_name = industry or "Unknown"
company_name_en = company_name or ticker
stocks_data[ticker] = {
"weight": round(float(weight), 4),
@ -125,7 +120,8 @@ def generate_heatmap_data(target_date):
"marketCap": round(float(market_cap), 2),
"sector": sector_name,
"sectorCode": get_sector_code(sector_name),
"industry": industry_name
"industry": industry_name,
"companyName": company_name_en
}
total_market_cap += float(market_cap)

View File

@ -74,32 +74,23 @@ def get_dates_for_month(year, month):
return dates
def get_sector_code(sector):
"""섹터명을 2자리 코드로 변환 (yfinance + GICS 혼용 지원)"""
"""섹터명을 2자리 코드로 변환 (yfinance 형식)"""
if not sector:
return "OT"
# yfinance 섹터명 + GICS 섹터명 혼용 매핑
# yfinance 섹터명 매핑 (DB 실제 값 기준)
sector_map = {
# yfinance 섹터명
"Technology": "IT",
"Healthcare": "HC",
"Financial Services": "FN",
"Consumer Cyclical": "CD",
"Industrials": "IN",
"Communication Services": "CS",
"Consumer Defensive": "CP",
"Energy": "EN",
"Utilities": "UT",
"Real Estate": "RE",
"Basic Materials": "MT",
# GICS 표준 섹터명 (호환성)
"Information Technology": "IT",
"Health Care": "HC",
"Financials": "FN",
"Consumer Discretionary": "CD",
"Consumer Staples": "CP",
"Materials": "MT"
"TECHNOLOGY": "IT",
"HEALTHCARE": "HC",
"FINANCIAL": "FN",
"CONSUMER CYCLICAL": "CD",
"INDUSTRIALS": "IN",
"COMMUNICATION SERVICES": "CS",
"CONSUMER DEFENSIVE": "CP",
"ENERGY": "EN",
"UTILITIES": "UT",
"REAL ESTATE": "RE",
"BASIC MATERIALS": "MT",
}
return sector_map.get(sector, "OT") # OT = Other
@ -114,7 +105,7 @@ def generate_heatmap_data(target_date):
conn = DBConn()
# S&P 500 시가총액 + 캔들 종가 + 섹터/산업 조인 쿼리
# S&P 500 시가총액 + 캔들 종가 + 섹터/산업 + 회사명 조인 쿼리
sql = """
SELECT
h.invest_code,
@ -122,12 +113,15 @@ def generate_heatmap_data(target_date):
c.close,
h.market_cap_usd,
h.sector,
h.industry
h.industry,
p.code_desc_en
FROM stock_market_cap_history h
INNER JOIN invest_candles c
ON h.invest_code = c.invest_code
AND h.snapshot_date = c.target_dt
AND c.interval = '1d'
INNER JOIN invest_product_code p
ON h.invest_code = p.invest_code
WHERE h.snapshot_date = %s
AND h.sp500_weight_percent IS NOT NULL
ORDER BY h.sp500_weight_percent DESC
@ -145,9 +139,10 @@ def generate_heatmap_data(target_date):
total_market_cap = 0
sectors_set = set()
for ticker, weight, close_price, market_cap, sector, industry in records:
for ticker, weight, close_price, market_cap, sector, industry, company_name in records:
sector_name = sector or "Unknown"
industry_name = industry or "Unknown"
company_name_en = company_name or ticker
stocks_data[ticker] = {
"weight": round(float(weight), 4),
@ -155,7 +150,8 @@ def generate_heatmap_data(target_date):
"marketCap": round(float(market_cap), 2),
"sector": sector_name,
"sectorCode": get_sector_code(sector_name),
"industry": industry_name
"industry": industry_name,
"companyName": company_name_en
}
total_market_cap += float(market_cap)

View File

@ -0,0 +1,55 @@
"""
Get detailed Technology sector stock list grouped by industry
"""
import psycopg2
import sys
# Fix Windows console encoding
if sys.platform == 'win32':
sys.stdout.reconfigure(encoding='utf-8')
# Connect to database
conn = psycopg2.connect(
host='3.38.180.110',
port=8088,
database='if_invest',
user='eldsoft',
password='eld240510'
)
cursor = conn.cursor()
# Query Technology sector stocks grouped by industry
query = """
SELECT industry, invest_code, code_desc_en
FROM invest_product_code
WHERE is_sp500 = 'Y' AND sector = 'TECHNOLOGY'
ORDER BY industry, invest_code;
"""
cursor.execute(query)
results = cursor.fetchall()
# Group by industry
by_industry = {}
for industry, ticker, name in results:
if industry not in by_industry:
by_industry[industry] = []
by_industry[industry].append((ticker, name))
# Print results
print("=" * 100)
print("TECHNOLOGY SECTOR - COMPLETE STOCK LIST BY INDUSTRY")
print("=" * 100)
print(f"\nTotal Industries: {len(by_industry)}")
print(f"Total Stocks: {len(results)}\n")
for industry, stocks in sorted(by_industry.items()):
print(f"\n{industry} ({len(stocks)} stocks):")
print("-" * 100)
for ticker, name in stocks:
print(f" {ticker:8s} : {name}")
cursor.close()
conn.close()

View File

@ -0,0 +1,784 @@
2025-10-10 21:29:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:22 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:22 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:30:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:36:44 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:36:45 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:37:21 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:37:49 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:03 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:04 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:15 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:15 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:15 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:15 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:15 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:15 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:16 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:16 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:16 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:16 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:16 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:16 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:17 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:17 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:17 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:17 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:17 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:17 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:18 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:18 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:18 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:18 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:18 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:18 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:19 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:19 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:19 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:19 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:19 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:19 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:20 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:20 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:20 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:20 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:20 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:20 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:21 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:21 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:21 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:21 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:21 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:22 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:22 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:22 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:22 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:22 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:26 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:26 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:26 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:26 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:26 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:26 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:27 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:27 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:27 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:27 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:27 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:27 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:28 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:28 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:28 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:28 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:28 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:28 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:29 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:29 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:29 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:29 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:29 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:29 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:30 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:30 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:30 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:30 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:30 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:30 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:31 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:31 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:31 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:31 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:31 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:31 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:32 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:32 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:32 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:32 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:32 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:32 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:33 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:33 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:33 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:33 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:33 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:33 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:34 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:34 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:34 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:34 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:34 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:34 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:35 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:35 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:35 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:35 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:35 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:35 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:36 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:36 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:36 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:36 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:36 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:36 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:37 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:37 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:37 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:37 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:37 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:37 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:38 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:38 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:38 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:38 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:38 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:38 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:39 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:39 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:39 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:39 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:40 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:40 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:40 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:40 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:40 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:40 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:41 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:41 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:41 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:41 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:41 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:42 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:42 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:42 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:42 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:42 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:42 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:43 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:43 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:43 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:43 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:43 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:43 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:44 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:44 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:44 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:44 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:44 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:44 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:45 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:45 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:45 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:45 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:45 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:45 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:46 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:46 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:46 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:46 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:46 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:46 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:47 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:47 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:47 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:47 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:47 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:47 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:48 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:48 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:48 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:48 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:48 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:48 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:49 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:49 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:49 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:49 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:49 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:50 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:50 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:50 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:50 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:50 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:50 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:51 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:51 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:51 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:51 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:51 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:51 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:52 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:52 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:52 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:52 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:52 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:52 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:53 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:53 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:53 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:53 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:53 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:53 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:54 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:54 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:54 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:54 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:54 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:54 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:55 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:55 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:55 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:55 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:55 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:55 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:56 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:56 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:56 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:56 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:57 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:57 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:57 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:57 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:57 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:57 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:58 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:58 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:58 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:58 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:58 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:58 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:59 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:59 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:59 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:59 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:59 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:38:59 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:00 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:00 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:00 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:00 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:00 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:00 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:01 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:01 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:01 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:01 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:01 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:01 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:02 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:02 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:02 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:02 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:02 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:02 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:03 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:03 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:03 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:03 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:03 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:03 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:04 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:04 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:04 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:04 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:04 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:04 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:15 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:15 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:15 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:15 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:15 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:16 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:16 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:16 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:16 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:16 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:16 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:17 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:17 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:17 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:17 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:17 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:17 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:18 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:18 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:18 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:18 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:18 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:18 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:19 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:19 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:19 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:19 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:19 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:19 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:19 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:20 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:20 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:20 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:20 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:20 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:20 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:21 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:21 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:21 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:21 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:21 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:21 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:22 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:22 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:22 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:22 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:22 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:22 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:23 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:24 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:25 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:26 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:26 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:26 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:26 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:26 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:26 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:27 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:27 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:27 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:27 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:27 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:27 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:28 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:28 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:28 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:28 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:28 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:28 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:28 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:29 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:29 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:29 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:29 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:29 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:29 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:30 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:30 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:30 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:30 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:31 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:31 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:31 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:31 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:31 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:31 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:31 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:32 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:32 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:32 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:32 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:32 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:33 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:33 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:33 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:33 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:33 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:33 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:34 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:34 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:34 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:34 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:34 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:34 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:35 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:35 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:35 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:35 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:35 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:35 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:36 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:36 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:36 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:36 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:36 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:36 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:37 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:37 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:37 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:37 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:37 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:37 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:38 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:38 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:38 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:38 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:38 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:38 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:39 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:39 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:39 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:39 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:39 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:39 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:40 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:40 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:40 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:40 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:40 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:40 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:41 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:41 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:41 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:41 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:41 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:41 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:42 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:42 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:42 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:42 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:42 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:42 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:43 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:43 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:43 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:43 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:43 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:43 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:44 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:44 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:44 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:44 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:44 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:45 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:45 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:45 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:45 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:45 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:45 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:46 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:46 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:46 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:46 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:46 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:46 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:47 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:47 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:47 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:47 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:48 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:48 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:48 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:48 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:48 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:48 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:48 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:49 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:49 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:49 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:49 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:49 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:49 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:50 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:50 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:50 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:50 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:50 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:50 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:51 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:51 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:51 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:51 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:51 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:51 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:52 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:52 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:52 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:52 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:52 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:53 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:53 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:53 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:53 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:53 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:53 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:53 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:54 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:54 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:54 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:54 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:54 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:54 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:55 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:55 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:55 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:55 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:55 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:56 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:56 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:56 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:56 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:56 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:57 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:57 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:57 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:57 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:57 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:57 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:57 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:58 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:58 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:58 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:58 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:58 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:59 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:59 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:59 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:59 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:39:59 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:00 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:00 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:00 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:00 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:00 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:01 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:01 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:01 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:01 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:01 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:02 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:02 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:02 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:02 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:02 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:03 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:03 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:03 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:03 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:03 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:03 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:04 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:04 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:04 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:04 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:04 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:04 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:06 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:07 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:08 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:09 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:10 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:11 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:12 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:13 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 21:40:14 - INFO - DB 연결이 닫혔습니다.
2025-10-10 22:37:17 - INFO - DB 연결이 닫혔습니다.
2025-10-10 22:40:05 - INFO - DB 연결이 닫혔습니다.
2025-10-10 22:40:05 - INFO - DB 연결이 닫혔습니다.

View File

@ -0,0 +1,9 @@
2025-10-11 01:35:31 - INFO - DB 연결이 닫혔습니다.
2025-10-11 01:35:41 - INFO - DB 연결이 닫혔습니다.
2025-10-11 14:03:05 - INFO - DB 연결이 닫혔습니다.
2025-10-11 14:03:08 - INFO - DB 연결이 닫혔습니다.
2025-10-11 14:03:13 - INFO - DB 연결이 닫혔습니다.
2025-10-11 14:03:15 - INFO - DB 연결이 닫혔습니다.
2025-10-11 14:03:45 - INFO - DB 연결이 닫혔습니다.
2025-10-11 14:03:56 - INFO - DB 연결이 닫혔습니다.
2025-10-11 14:13:58 - INFO - DB 연결이 닫혔습니다.

View File

@ -0,0 +1,74 @@
2025-10-10 21:38:03 - INFO - ================================================================================
2025-10-10 21:38:03 - INFO - S&P 500 Heatmap - 전체 히스토리 JSON 생성
2025-10-10 21:38:03 - INFO - ================================================================================
2025-10-10 21:38:03 - INFO - 전체 데이터 범위: 2022-10-04 ~ 2025-10-09
2025-10-10 21:38:03 - INFO - 총 757개 날짜
2025-10-10 21:38:03 - INFO -
[진행중] 1/757 - 2022-10-04
2025-10-10 21:38:21 - INFO -
[진행중] 100/757 - 2023-02-27
2025-10-10 21:38:21 - INFO - 성공: 100, 실패: 0, 스킵: 0
2025-10-10 21:38:38 - INFO -
[진행중] 200/757 - 2023-07-21
2025-10-10 21:38:39 - INFO - 성공: 200, 실패: 0, 스킵: 0
2025-10-10 21:38:56 - INFO -
[진행중] 300/757 - 2023-12-12
2025-10-10 21:38:56 - INFO - 성공: 300, 실패: 0, 스킵: 0
2025-10-10 21:39:12 - INFO -
[진행중] 400/757 - 2024-05-07
2025-10-10 21:39:13 - INFO - 성공: 400, 실패: 0, 스킵: 0
2025-10-10 21:39:29 - INFO -
[진행중] 500/757 - 2024-09-30
2025-10-10 21:39:29 - INFO - 성공: 500, 실패: 0, 스킵: 0
2025-10-10 21:39:46 - INFO -
[진행중] 600/757 - 2025-02-25
2025-10-10 21:39:46 - INFO - 성공: 600, 실패: 0, 스킵: 0
2025-10-10 21:40:04 - INFO -
[진행중] 700/757 - 2025-07-21
2025-10-10 21:40:04 - INFO - 성공: 700, 실패: 0, 스킵: 0
2025-10-10 21:40:14 - INFO -
================================================================================
2025-10-10 21:40:14 - INFO - 전체 처리 완료
2025-10-10 21:40:14 - INFO - 성공: 757, 실패: 0, 스킵: 0
2025-10-10 21:40:14 - INFO - ================================================================================
2025-10-10 21:40:14 - INFO -
[월별 생성 파일 수]
2025-10-10 21:40:14 - INFO - 2022-10: 20개 파일
2025-10-10 21:40:14 - INFO - 2022-11: 21개 파일
2025-10-10 21:40:14 - INFO - 2022-12: 21개 파일
2025-10-10 21:40:14 - INFO - 2023-01: 20개 파일
2025-10-10 21:40:14 - INFO - 2023-02: 19개 파일
2025-10-10 21:40:14 - INFO - 2023-03: 23개 파일
2025-10-10 21:40:14 - INFO - 2023-04: 19개 파일
2025-10-10 21:40:14 - INFO - 2023-05: 22개 파일
2025-10-10 21:40:14 - INFO - 2023-06: 21개 파일
2025-10-10 21:40:14 - INFO - 2023-07: 20개 파일
2025-10-10 21:40:14 - INFO - 2023-08: 23개 파일
2025-10-10 21:40:14 - INFO - 2023-09: 20개 파일
2025-10-10 21:40:14 - INFO - 2023-10: 22개 파일
2025-10-10 21:40:14 - INFO - 2023-11: 21개 파일
2025-10-10 21:40:14 - INFO - 2023-12: 20개 파일
2025-10-10 21:40:14 - INFO - 2024-01: 21개 파일
2025-10-10 21:40:14 - INFO - 2024-02: 20개 파일
2025-10-10 21:40:14 - INFO - 2024-03: 20개 파일
2025-10-10 21:40:14 - INFO - 2024-04: 22개 파일
2025-10-10 21:40:14 - INFO - 2024-05: 22개 파일
2025-10-10 21:40:14 - INFO - 2024-06: 19개 파일
2025-10-10 21:40:14 - INFO - 2024-07: 22개 파일
2025-10-10 21:40:14 - INFO - 2024-08: 22개 파일
2025-10-10 21:40:14 - INFO - 2024-09: 20개 파일
2025-10-10 21:40:14 - INFO - 2024-10: 23개 파일
2025-10-10 21:40:14 - INFO - 2024-11: 20개 파일
2025-10-10 21:40:14 - INFO - 2024-12: 21개 파일
2025-10-10 21:40:14 - INFO - 2025-01: 20개 파일
2025-10-10 21:40:14 - INFO - 2025-02: 19개 파일
2025-10-10 21:40:14 - INFO - 2025-03: 21개 파일
2025-10-10 21:40:14 - INFO - 2025-04: 21개 파일
2025-10-10 21:40:14 - INFO - 2025-05: 21개 파일
2025-10-10 21:40:14 - INFO - 2025-06: 20개 파일
2025-10-10 21:40:14 - INFO - 2025-07: 22개 파일
2025-10-10 21:40:14 - INFO - 2025-08: 21개 파일
2025-10-10 21:40:14 - INFO - 2025-09: 21개 파일
2025-10-10 21:40:14 - INFO - 2025-10: 7개 파일
2025-10-10 21:40:14 - INFO -
총 37개월, 757개 파일 생성

View File

@ -0,0 +1,61 @@
2025-10-10 21:30:13 - INFO - ================================================================================
2025-10-10 21:30:13 - INFO - S&P 500 Heatmap JSON Generation - 최근 1일
2025-10-10 21:30:13 - INFO - ================================================================================
2025-10-10 21:30:13 - INFO - 최근 1일치 데이터: 1개 날짜 (2025-10-09 ~ 2025-10-09)
2025-10-10 21:30:13 - INFO - 2025-10-09 - 5개 종목, 3개 섹터, 총 시총 $4,500.87B
2025-10-10 21:30:14 - INFO - [S3 업로드] api/sp500/2025/10/heatmap_20251009.json - 5개 종목, 3개 섹터
2025-10-10 21:30:14 - INFO -
================================================================================
2025-10-10 21:30:14 - INFO - 최근 1일 처리 완료
2025-10-10 21:30:14 - INFO - 성공: 1, 실패: 0
2025-10-10 21:30:14 - INFO - ================================================================================
2025-10-10 21:30:22 - INFO - ================================================================================
2025-10-10 21:30:22 - INFO - S&P 500 Heatmap JSON Generation - 최근 21일
2025-10-10 21:30:22 - INFO - ================================================================================
2025-10-10 21:30:22 - INFO - 최근 21일치 데이터: 15개 날짜 (2025-09-19 ~ 2025-10-09)
2025-10-10 21:30:22 - INFO - 2025-10-09 - 5개 종목, 3개 섹터, 총 시총 $4,500.87B
2025-10-10 21:30:23 - INFO - [S3 업로드] api/sp500/2025/10/heatmap_20251009.json - 5개 종목, 3개 섹터
2025-10-10 21:30:23 - INFO - 2025-10-08 - 5개 종목, 3개 섹터, 총 시총 $4,563.31B
2025-10-10 21:30:23 - INFO - [S3 업로드] api/sp500/2025/10/heatmap_20251008.json - 5개 종목, 3개 섹터
2025-10-10 21:30:23 - INFO - 2025-10-07 - 5개 종목, 3개 섹터, 총 시총 $4,539.80B
2025-10-10 21:30:23 - INFO - [S3 업로드] api/sp500/2025/10/heatmap_20251007.json - 5개 종목, 3개 섹터
2025-10-10 21:30:23 - INFO - 2025-10-06 - 5개 종목, 3개 섹터, 총 시총 $4,540.58B
2025-10-10 21:30:23 - INFO - [S3 업로드] api/sp500/2025/10/heatmap_20251006.json - 5개 종목, 3개 섹터
2025-10-10 21:30:23 - INFO - 2025-10-03 - 503개 종목, 15개 섹터, 총 시총 $59,538.07B
2025-10-10 21:30:23 - INFO - [S3 업로드] api/sp500/2025/10/heatmap_20251003.json - 503개 종목, 15개 섹터
2025-10-10 21:30:23 - INFO - 2025-10-02 - 503개 종목, 15개 섹터, 총 시총 $59,531.25B
2025-10-10 21:30:23 - INFO - [S3 업로드] api/sp500/2025/10/heatmap_20251002.json - 503개 종목, 15개 섹터
2025-10-10 21:30:24 - INFO - 2025-10-01 - 503개 종목, 15개 섹터, 총 시총 $59,511.20B
2025-10-10 21:30:24 - INFO - [S3 업로드] api/sp500/2025/10/heatmap_20251001.json - 503개 종목, 15개 섹터
2025-10-10 21:30:24 - INFO - 2025-09-30 - 503개 종목, 15개 섹터, 총 시총 $59,295.35B
2025-10-10 21:30:24 - INFO - [S3 업로드] api/sp500/2025/09/heatmap_20250930.json - 503개 종목, 15개 섹터
2025-10-10 21:30:24 - INFO - 2025-09-29 - 503개 종목, 15개 섹터, 총 시총 $59,061.86B
2025-10-10 21:30:24 - INFO - [S3 업로드] api/sp500/2025/09/heatmap_20250929.json - 503개 종목, 15개 섹터
2025-10-10 21:30:24 - INFO - 2025-09-26 - 503개 종목, 15개 섹터, 총 시총 $58,904.97B
2025-10-10 21:30:24 - INFO - [S3 업로드] api/sp500/2025/09/heatmap_20250926.json - 503개 종목, 15개 섹터
2025-10-10 21:30:25 - INFO - 2025-09-25 - 503개 종목, 15개 섹터, 총 시총 $58,563.15B
2025-10-10 21:30:25 - INFO - [S3 업로드] api/sp500/2025/09/heatmap_20250925.json - 503개 종목, 15개 섹터
2025-10-10 21:30:25 - INFO - 2025-09-24 - 503개 종목, 15개 섹터, 총 시총 $58,881.48B
2025-10-10 21:30:25 - INFO - [S3 업로드] api/sp500/2025/09/heatmap_20250924.json - 503개 종목, 15개 섹터
2025-10-10 21:30:25 - INFO - 2025-09-23 - 503개 종목, 15개 섹터, 총 시총 $59,051.27B
2025-10-10 21:30:25 - INFO - [S3 업로드] api/sp500/2025/09/heatmap_20250923.json - 503개 종목, 15개 섹터
2025-10-10 21:30:25 - INFO - 2025-09-22 - 503개 종목, 15개 섹터, 총 시총 $59,393.14B
2025-10-10 21:30:25 - INFO - [S3 업로드] api/sp500/2025/09/heatmap_20250922.json - 503개 종목, 15개 섹터
2025-10-10 21:30:25 - INFO - 2025-09-19 - 503개 종목, 15개 섹터, 총 시총 $59,117.44B
2025-10-10 21:30:25 - INFO - [S3 업로드] api/sp500/2025/09/heatmap_20250919.json - 503개 종목, 15개 섹터
2025-10-10 21:30:25 - INFO -
================================================================================
2025-10-10 21:30:25 - INFO - 최근 21일 처리 완료
2025-10-10 21:30:25 - INFO - 성공: 15, 실패: 0
2025-10-10 21:30:25 - INFO - ================================================================================
2025-10-10 22:40:05 - INFO - ================================================================================
2025-10-10 22:40:05 - INFO - S&P 500 Heatmap JSON Generation - 최근 1일
2025-10-10 22:40:05 - INFO - ================================================================================
2025-10-10 22:40:05 - INFO - 최근 1일치 데이터: 1개 날짜 (2025-10-09 ~ 2025-10-09)
2025-10-10 22:40:05 - INFO - 2025-10-09 - 5개 종목, 3개 섹터, 총 시총 $4,500.87B
2025-10-10 22:40:05 - INFO - [S3 업로드] api/sp500/2025/10/heatmap_20251009.json - 5개 종목, 3개 섹터
2025-10-10 22:40:05 - INFO -
================================================================================
2025-10-10 22:40:05 - INFO - 최근 1일 처리 완료
2025-10-10 22:40:05 - INFO - 성공: 1, 실패: 0
2025-10-10 22:40:05 - INFO - ================================================================================

View File

@ -0,0 +1,28 @@
2025-10-10 21:37:16 - INFO - ================================================================================
2025-10-10 21:37:16 - INFO - stock_market_cap_history 테이블 sector/industry 동기화 시작
2025-10-10 21:37:16 - INFO - ================================================================================
2025-10-10 21:37:16 - INFO - 업데이트 대상 레코드: 377,505개
2025-10-10 21:37:21 - ERROR - 동기화 실패: no results to fetch
2025-10-10 21:37:42 - INFO - ================================================================================
2025-10-10 21:37:42 - INFO - stock_market_cap_history 테이블 sector/industry 동기화 시작
2025-10-10 21:37:42 - INFO - ================================================================================
2025-10-10 21:37:42 - INFO - 업데이트 대상 레코드: 377,505개
2025-10-10 21:37:49 - INFO - ✓ 377,505개 레코드 업데이트 완료
2025-10-10 21:37:49 - INFO -
업데이트 후 Sector 목록:
2025-10-10 21:37:49 - INFO - --------------------------------------------------------------------------------
2025-10-10 21:37:49 - INFO - - BASIC MATERIALS
2025-10-10 21:37:49 - INFO - - COMMUNICATION SERVICES
2025-10-10 21:37:49 - INFO - - CONSUMER CYCLICAL
2025-10-10 21:37:49 - INFO - - CONSUMER DEFENSIVE
2025-10-10 21:37:49 - INFO - - ENERGY
2025-10-10 21:37:49 - INFO - - FINANCIAL
2025-10-10 21:37:49 - INFO - - HEALTHCARE
2025-10-10 21:37:49 - INFO - - INDUSTRIALS
2025-10-10 21:37:49 - INFO - - REAL ESTATE
2025-10-10 21:37:49 - INFO - - TECHNOLOGY
2025-10-10 21:37:49 - INFO - - UTILITIES
2025-10-10 21:37:49 - INFO -
================================================================================
2025-10-10 21:37:49 - INFO - 동기화 완료
2025-10-10 21:37:49 - INFO - ================================================================================

View File

@ -0,0 +1,225 @@
================================================================================
S&P 500 SECTOR AND INDUSTRY VERIFICATION REPORT
Generated: 2025-10-11 14:14:22
================================================================================
[OK] Connected to database: if_invest
1. UNIQUE SECTOR NAMES (EXACT DATABASE VALUES)
--------------------------------------------------------------------------------
1. 'BASIC MATERIALS'
2. 'COMMUNICATION SERVICES'
3. 'CONSUMER CYCLICAL'
4. 'CONSUMER DEFENSIVE'
5. 'ENERGY'
6. 'FINANCIAL'
7. 'HEALTHCARE'
8. 'INDUSTRIALS'
9. 'REAL ESTATE'
10. 'TECHNOLOGY'
11. 'UTILITIES'
2. STOCK COUNT PER SECTOR
--------------------------------------------------------------------------------
Sector Name Stock Count
--------------------------------------------------------------------------------
TECHNOLOGY 82
INDUSTRIALS 71
FINANCIAL 69
HEALTHCARE 60
CONSUMER CYCLICAL 55
CONSUMER DEFENSIVE 37
UTILITIES 31
REAL ESTATE 31
COMMUNICATION SERVICES 25
ENERGY 22
BASIC MATERIALS 20
--------------------------------------------------------------------------------
TOTAL 503
3. INDUSTRY BREAKDOWN PER SECTOR
--------------------------------------------------------------------------------
[BASIC MATERIALS]
- SPECIALTY CHEMICALS: 10 stocks
- AGRICULTURAL INPUTS: 3 stocks
- BUILDING MATERIALS: 2 stocks
- STEEL: 2 stocks
- COPPER: 1 stocks
- GOLD: 1 stocks
- CHEMICALS: 1 stocks
[COMMUNICATION SERVICES]
- ENTERTAINMENT: 10 stocks
- TELECOM SERVICES: 5 stocks
- ADVERTISING AGENCIES: 4 stocks
- INTERNET CONTENT & INFORMATION: 4 stocks
- ELECTRONIC GAMING & MULTIMEDIA: 2 stocks
[CONSUMER CYCLICAL]
- TRAVEL SERVICES: 6 stocks
- PACKAGING & CONTAINERS: 6 stocks
- RESTAURANTS: 6 stocks
- AUTO PARTS: 5 stocks
- SPECIALTY RETAIL: 4 stocks
- RESIDENTIAL CONSTRUCTION: 4 stocks
- INTERNET RETAIL: 3 stocks
- AUTO MANUFACTURERS: 3 stocks
- APPAREL RETAIL: 3 stocks
- RESORTS & CASINOS: 3 stocks
- HOME IMPROVEMENT RETAIL: 2 stocks
- LODGING: 2 stocks
- FOOTWEAR & ACCESSORIES: 2 stocks
- FURNISHINGS, FIXTURES & APPLIANCES: 1 stocks
- LEISURE: 1 stocks
- PERSONAL SERVICES: 1 stocks
- LUXURY GOODS: 1 stocks
- APPAREL MANUFACTURING: 1 stocks
- AUTO & TRUCK DEALERSHIPS: 1 stocks
[CONSUMER DEFENSIVE]
- PACKAGED FOODS: 9 stocks
- HOUSEHOLD & PERSONAL PRODUCTS: 7 stocks
- DISCOUNT STORES: 5 stocks
- BEVERAGES-NON-ALCOHOLIC: 4 stocks
- FARM PRODUCTS: 3 stocks
- CONFECTIONERS: 2 stocks
- BEVERAGES-BREWERS: 2 stocks
- TOBACCO: 2 stocks
- FOOD DISTRIBUTION: 1 stocks
- BEVERAGES-WINERIES & DISTILLERIES: 1 stocks
- GROCERY STORES: 1 stocks
[ENERGY]
- OIL & GAS E&P: 10 stocks
- OIL & GAS MIDSTREAM: 4 stocks
- OIL & GAS EQUIPMENT & SERVICES: 3 stocks
- OIL & GAS REFINING & MARKETING: 3 stocks
- OIL & GAS INTEGRATED: 2 stocks
[FINANCIAL]
- ASSET MANAGEMENT: 12 stocks
- FINANCIAL DATA & STOCK EXCHANGES: 9 stocks
- BANKS-REGIONAL: 9 stocks
- INSURANCE-PROPERTY & CASUALTY: 9 stocks
- CREDIT SERVICES: 6 stocks
- INSURANCE BROKERS: 6 stocks
- CAPITAL MARKETS: 5 stocks
- BANKS-DIVERSIFIED: 5 stocks
- INSURANCE-LIFE: 4 stocks
- INSURANCE-DIVERSIFIED: 3 stocks
- INSURANCE-REINSURANCE: 1 stocks
[HEALTHCARE]
- DIAGNOSTICS & RESEARCH: 11 stocks
- MEDICAL DEVICES: 10 stocks
- MEDICAL INSTRUMENTS & SUPPLIES: 9 stocks
- DRUG MANUFACTURERS-GENERAL: 9 stocks
- HEALTHCARE PLANS: 7 stocks
- BIOTECHNOLOGY: 5 stocks
- MEDICAL DISTRIBUTION: 4 stocks
- MEDICAL CARE FACILITIES: 3 stocks
- DRUG MANUFACTURERS-SPECIALTY & GENERIC: 2 stocks
[INDUSTRIALS]
- SPECIALTY INDUSTRIAL MACHINERY: 17 stocks
- AEROSPACE & DEFENSE: 12 stocks
- BUILDING PRODUCTS & EQUIPMENT: 6 stocks
- INTEGRATED FREIGHT & LOGISTICS: 5 stocks
- RAILROADS: 4 stocks
- INDUSTRIAL DISTRIBUTION: 3 stocks
- AIRLINES: 3 stocks
- ENGINEERING & CONSTRUCTION: 3 stocks
- FARM & HEAVY CONSTRUCTION MACHINERY: 3 stocks
- CONGLOMERATES: 2 stocks
- WASTE MANAGEMENT: 2 stocks
- TOOLS & ACCESSORIES: 2 stocks
- CONSULTING SERVICES: 2 stocks
- SPECIALTY BUSINESS SERVICES: 2 stocks
- ELECTRICAL EQUIPMENT & PARTS: 1 stocks
- SECURITY & PROTECTION SERVICES: 1 stocks
- POLLUTION & TREATMENT CONTROLS: 1 stocks
- RENTAL & LEASING SERVICES: 1 stocks
- TRUCKING: 1 stocks
[REAL ESTATE]
- REIT-RESIDENTIAL: 7 stocks
- REIT-SPECIALTY: 7 stocks
- REIT-RETAIL: 5 stocks
- REIT-INDUSTRIAL: 3 stocks
- REIT-HEALTHCARE FACILITIES: 3 stocks
- REIT-OFFICE: 2 stocks
- REAL ESTATE SERVICES: 2 stocks
- REIT-HOTEL & MOTEL: 1 stocks
- REIT-DIVERSIFIED: 1 stocks
[TECHNOLOGY]
- SOFTWARE-APPLICATION: 17 stocks
- SOFTWARE-INFRASTRUCTURE: 16 stocks
- SEMICONDUCTORS: 13 stocks
- INFORMATION TECHNOLOGY SERVICES: 11 stocks
- COMPUTER HARDWARE: 6 stocks
- SCIENTIFIC & TECHNICAL INSTRUMENTS: 5 stocks
- ELECTRONIC COMPONENTS: 4 stocks
- SEMICONDUCTOR EQUIPMENT & MATERIALS: 4 stocks
- COMMUNICATION EQUIPMENT: 4 stocks
- SOLAR: 1 stocks
- CONSUMER ELECTRONICS: 1 stocks
[UTILITIES]
- UTILITIES - REGULATED ELECTRIC: 23 stocks
- UTILITIES - INDEPENDENT POWER PRODUCERS: 2 stocks
- UTILITIES - REGULATED GAS: 2 stocks
- UTILITIES - DIVERSIFIED: 2 stocks
- UTILITIES - RENEWABLE: 1 stocks
- UTILITIES - REGULATED WATER: 1 stocks
4. TOTAL S&P 500 STOCK COUNT
--------------------------------------------------------------------------------
Total S&P 500 stocks: 503
5. DATA QUALITY CHECKS
--------------------------------------------------------------------------------
Stocks with NULL or empty sector: 0
Stocks with NULL or empty industry: 0
6. SAMPLE STOCKS PER SECTOR (First 3 per sector)
--------------------------------------------------------------------------------
[BASIC MATERIALS]
ALB Albemarle Corporation (SPECIALTY CHEMICALS)
APD Air Products (SPECIALTY CHEMICALS)
CF CF Industries (AGRICULTURAL INPUTS)
[COMMUNICATION SERVICES]
APP AppLovin (ADVERTISING AGENCIES)
CHTR Charter Communications (TELECOM SERVICES)
CMCSA Comcast Corp. (TELECOM SERVICES)
[CONSUMER CYCLICAL]
ABNB Airbnb, Inc. (TRAVEL SERVICES)
AMCR Amcor (PACKAGING & CONTAINERS)
AMZN Amazon.com, Inc. (INTERNET RETAIL)
[CONSUMER DEFENSIVE]
ADM Archer Daniels Midland (FARM PRODUCTS)
Traceback (most recent call last):
File "C:\Users\aofhd\OneDrive\바탕 화면\dev\if_invest\백엔드\verify_sp500_sectors.py", line 178, in <module>
main()
~~~~^^
File "C:\Users\aofhd\OneDrive\바탕 화면\dev\if_invest\백엔드\verify_sp500_sectors.py", line 170, in main
print(f" {ticker:<8} {name:<40} ({industry})")
~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
UnicodeEncodeError: 'cp949' codec can't encode character '\u2013' in position 16: illegal multibyte sequence

View File

@ -0,0 +1,176 @@
{
"technology_sector": {
"total_industries": 0,
"total_stocks": 0,
"industries": {}
},
"all_sectors": {
"BASIC MATERIALS": {
"industry_count": 7,
"industries": {
"SPECIALTY CHEMICALS": 10,
"AGRICULTURAL INPUTS": 3,
"BUILDING MATERIALS": 2,
"STEEL": 2,
"COPPER": 1,
"GOLD": 1,
"CHEMICALS": 1
}
},
"COMMUNICATION SERVICES": {
"industry_count": 5,
"industries": {
"ENTERTAINMENT": 10,
"TELECOM SERVICES": 5,
"ADVERTISING AGENCIES": 4,
"INTERNET CONTENT & INFORMATION": 4,
"ELECTRONIC GAMING & MULTIMEDIA": 2
}
},
"CONSUMER CYCLICAL": {
"industry_count": 19,
"industries": {
"TRAVEL SERVICES": 6,
"PACKAGING & CONTAINERS": 6,
"RESTAURANTS": 6,
"AUTO PARTS": 5,
"SPECIALTY RETAIL": 4,
"RESIDENTIAL CONSTRUCTION": 4,
"INTERNET RETAIL": 3,
"AUTO MANUFACTURERS": 3,
"APPAREL RETAIL": 3,
"RESORTS & CASINOS": 3,
"HOME IMPROVEMENT RETAIL": 2,
"LODGING": 2,
"FOOTWEAR & ACCESSORIES": 2,
"FURNISHINGS, FIXTURES & APPLIANCES": 1,
"LEISURE": 1,
"PERSONAL SERVICES": 1,
"LUXURY GOODS": 1,
"APPAREL MANUFACTURING": 1,
"AUTO & TRUCK DEALERSHIPS": 1
}
},
"CONSUMER DEFENSIVE": {
"industry_count": 11,
"industries": {
"PACKAGED FOODS": 9,
"HOUSEHOLD & PERSONAL PRODUCTS": 7,
"DISCOUNT STORES": 5,
"BEVERAGES-NON-ALCOHOLIC": 4,
"FARM PRODUCTS": 3,
"CONFECTIONERS": 2,
"BEVERAGES-BREWERS": 2,
"TOBACCO": 2,
"FOOD DISTRIBUTION": 1,
"BEVERAGES-WINERIES & DISTILLERIES": 1,
"GROCERY STORES": 1
}
},
"ENERGY": {
"industry_count": 5,
"industries": {
"OIL & GAS E&P": 10,
"OIL & GAS MIDSTREAM": 4,
"OIL & GAS EQUIPMENT & SERVICES": 3,
"OIL & GAS REFINING & MARKETING": 3,
"OIL & GAS INTEGRATED": 2
}
},
"FINANCIAL": {
"industry_count": 11,
"industries": {
"ASSET MANAGEMENT": 12,
"FINANCIAL DATA & STOCK EXCHANGES": 9,
"BANKS-REGIONAL": 9,
"INSURANCE-PROPERTY & CASUALTY": 9,
"CREDIT SERVICES": 6,
"INSURANCE BROKERS": 6,
"CAPITAL MARKETS": 5,
"BANKS-DIVERSIFIED": 5,
"INSURANCE-LIFE": 4,
"INSURANCE-DIVERSIFIED": 3,
"INSURANCE-REINSURANCE": 1
}
},
"HEALTHCARE": {
"industry_count": 9,
"industries": {
"DIAGNOSTICS & RESEARCH": 11,
"MEDICAL DEVICES": 10,
"MEDICAL INSTRUMENTS & SUPPLIES": 9,
"DRUG MANUFACTURERS-GENERAL": 9,
"HEALTHCARE PLANS": 7,
"BIOTECHNOLOGY": 5,
"MEDICAL DISTRIBUTION": 4,
"MEDICAL CARE FACILITIES": 3,
"DRUG MANUFACTURERS-SPECIALTY & GENERIC": 2
}
},
"INDUSTRIALS": {
"industry_count": 19,
"industries": {
"SPECIALTY INDUSTRIAL MACHINERY": 17,
"AEROSPACE & DEFENSE": 12,
"BUILDING PRODUCTS & EQUIPMENT": 6,
"INTEGRATED FREIGHT & LOGISTICS": 5,
"RAILROADS": 4,
"INDUSTRIAL DISTRIBUTION": 3,
"AIRLINES": 3,
"ENGINEERING & CONSTRUCTION": 3,
"FARM & HEAVY CONSTRUCTION MACHINERY": 3,
"CONGLOMERATES": 2,
"WASTE MANAGEMENT": 2,
"TOOLS & ACCESSORIES": 2,
"CONSULTING SERVICES": 2,
"SPECIALTY BUSINESS SERVICES": 2,
"ELECTRICAL EQUIPMENT & PARTS": 1,
"SECURITY & PROTECTION SERVICES": 1,
"POLLUTION & TREATMENT CONTROLS": 1,
"RENTAL & LEASING SERVICES": 1,
"TRUCKING": 1
}
},
"REAL ESTATE": {
"industry_count": 9,
"industries": {
"REIT-RESIDENTIAL": 7,
"REIT-SPECIALTY": 7,
"REIT-RETAIL": 5,
"REIT-INDUSTRIAL": 3,
"REIT-HEALTHCARE FACILITIES": 3,
"REIT-OFFICE": 2,
"REAL ESTATE SERVICES": 2,
"REIT-HOTEL & MOTEL": 1,
"REIT-DIVERSIFIED": 1
}
},
"TECHNOLOGY": {
"industry_count": 11,
"industries": {
"SOFTWARE-APPLICATION": 17,
"SOFTWARE-INFRASTRUCTURE": 16,
"SEMICONDUCTORS": 13,
"INFORMATION TECHNOLOGY SERVICES": 11,
"COMPUTER HARDWARE": 6,
"SCIENTIFIC & TECHNICAL INSTRUMENTS": 5,
"ELECTRONIC COMPONENTS": 4,
"SEMICONDUCTOR EQUIPMENT & MATERIALS": 4,
"COMMUNICATION EQUIPMENT": 4,
"SOLAR": 1,
"CONSUMER ELECTRONICS": 1
}
},
"UTILITIES": {
"industry_count": 6,
"industries": {
"UTILITIES - REGULATED ELECTRIC": 23,
"UTILITIES - INDEPENDENT POWER PRODUCERS": 2,
"UTILITIES - REGULATED GAS": 2,
"UTILITIES - DIVERSIFIED": 2,
"UTILITIES - RENEWABLE": 1,
"UTILITIES - REGULATED WATER": 1
}
}
}
}

View File

@ -0,0 +1,89 @@
"""
stock_market_cap_history 테이블의 sector와 industry를
invest_product_code 테이블의 값으로 동기화
목적:
- invest_product_code가 마스터 데이터
- stock_market_cap_history의 sector와 industry를 일괄 업데이트
"""
from db_conn import DBConn
from logger import get_logger
logger = get_logger("sync_sector_industry")
def sync_sector_industry():
"""sector와 industry 동기화"""
conn = DBConn()
logger.info("=" * 80)
logger.info("stock_market_cap_history 테이블 sector/industry 동기화 시작")
logger.info("=" * 80)
# 업데이트 쿼리
sql = """
UPDATE stock_market_cap_history h
SET
sector = p.sector,
industry = p.industry
FROM invest_product_code p
WHERE h.invest_code = p.invest_code
AND p.is_sp500 = 'Y'
AND (h.sector != p.sector OR h.industry != p.industry)
"""
try:
# 먼저 업데이트될 레코드 수 확인
check_sql = """
SELECT COUNT(*)
FROM stock_market_cap_history h
INNER JOIN invest_product_code p ON h.invest_code = p.invest_code
WHERE p.is_sp500 = 'Y'
AND (h.sector != p.sector OR h.industry != p.industry)
"""
result = conn.fetchone(check_sql)
count = result[0] if result else 0
logger.info(f"업데이트 대상 레코드: {count:,}")
if count == 0:
logger.info("업데이트할 레코드가 없습니다.")
conn.close()
return
# 업데이트 실행 (cursor 직접 사용)
conn.cursor.execute(sql)
conn.commit()
logger.info(f"{count:,}개 레코드 업데이트 완료")
# 검증: 현재 sector 종류 확인
verify_sql = """
SELECT DISTINCT sector
FROM stock_market_cap_history
ORDER BY sector
"""
sectors = conn.fetchall(verify_sql)
logger.info("\n업데이트 후 Sector 목록:")
logger.info("-" * 80)
for (sector,) in sectors:
logger.info(f" - {sector}")
conn.close()
logger.info("\n" + "=" * 80)
logger.info("동기화 완료")
logger.info("=" * 80)
except Exception as e:
logger.error(f"동기화 실패: {e}")
conn.rollback()
conn.close()
raise
if __name__ == "__main__":
sync_sector_industry()

View File

@ -0,0 +1,245 @@
"""
Verify S&P 500 Industry Subdivision Data in Database
Analyzes industry classifications for all sectors with focus on Technology sector
"""
import psycopg2
from typing import List, Dict, Tuple
import json
import sys
# Fix Windows console encoding
if sys.platform == 'win32':
sys.stdout.reconfigure(encoding='utf-8')
class DatabaseAnalyzer:
def __init__(self):
self.conn = None
self.cursor = None
def connect(self):
"""Connect to PostgreSQL database"""
try:
self.conn = psycopg2.connect(
host='3.38.180.110',
port=8088,
database='if_invest',
user='eldsoft',
password='eld240510'
)
self.cursor = self.conn.cursor()
print("✓ Database connection established")
except Exception as e:
print(f"✗ Database connection failed: {e}")
raise
def close(self):
"""Close database connection"""
if self.cursor:
self.cursor.close()
if self.conn:
self.conn.close()
print("\n✓ Database connection closed")
def query_technology_industries(self) -> List[Tuple]:
"""Query Technology sector industries with stock counts"""
query = """
SELECT industry, COUNT(*) as stock_count
FROM invest_product_code
WHERE is_sp500 = 'Y' AND sector = 'Information Technology'
GROUP BY industry
ORDER BY stock_count DESC;
"""
self.cursor.execute(query)
return self.cursor.fetchall()
def query_all_sectors_summary(self) -> List[Tuple]:
"""Query all sectors with industry counts"""
query = """
SELECT sector, COUNT(DISTINCT industry) as industry_count, COUNT(*) as stock_count
FROM invest_product_code
WHERE is_sp500 = 'Y'
GROUP BY sector
ORDER BY stock_count DESC;
"""
self.cursor.execute(query)
return self.cursor.fetchall()
def query_technology_stock_samples(self) -> List[Tuple]:
"""Query sample stocks per industry in Technology sector"""
query = """
SELECT industry, invest_code, code_desc_en
FROM invest_product_code
WHERE is_sp500 = 'Y' AND sector = 'Information Technology'
ORDER BY industry, invest_code;
"""
self.cursor.execute(query)
return self.cursor.fetchall()
def query_null_industries(self) -> int:
"""Check for NULL or empty industries"""
query = """
SELECT COUNT(*) as null_count
FROM invest_product_code
WHERE is_sp500 = 'Y' AND (industry IS NULL OR industry = '');
"""
self.cursor.execute(query)
result = self.cursor.fetchone()
return result[0] if result else 0
def query_all_industries_by_sector(self) -> Dict[str, List[Tuple]]:
"""Query all industries grouped by sector"""
query = """
SELECT sector, industry, COUNT(*) as stock_count
FROM invest_product_code
WHERE is_sp500 = 'Y'
GROUP BY sector, industry
ORDER BY sector, stock_count DESC;
"""
self.cursor.execute(query)
results = self.cursor.fetchall()
# Group by sector
by_sector = {}
for sector, industry, count in results:
if sector not in by_sector:
by_sector[sector] = []
by_sector[sector].append((industry, count))
return by_sector
def print_separator(self, title: str):
"""Print formatted section separator"""
print("\n" + "=" * 80)
print(f" {title}")
print("=" * 80)
def run_analysis(self):
"""Run complete database analysis"""
self.connect()
try:
# 1. Technology Sector Industries
self.print_separator("1. TECHNOLOGY SECTOR INDUSTRIES (Information Technology)")
tech_industries = self.query_technology_industries()
print(f"\nTotal Industries in Technology Sector: {len(tech_industries)}\n")
tech_total_stocks = 0
for industry, count in tech_industries:
print(f"{industry:50s} : {count:3d} stocks")
tech_total_stocks += count
print(f"\n TOTAL TECHNOLOGY STOCKS: {tech_total_stocks}")
# 2. All Sectors Summary
self.print_separator("2. ALL SECTORS SUMMARY")
sectors_summary = self.query_all_sectors_summary()
print(f"\nTotal Sectors: {len(sectors_summary)}\n")
total_stocks = 0
for sector, industry_count, stock_count in sectors_summary:
print(f"{sector:30s} : {industry_count:2d} industries, {stock_count:3d} stocks")
total_stocks += stock_count
print(f"\n TOTAL S&P 500 STOCKS: {total_stocks}")
# 3. Technology Stock Samples
self.print_separator("3. TECHNOLOGY SECTOR STOCK SAMPLES (Grouped by Industry)")
tech_samples = self.query_technology_stock_samples()
# Group by industry
by_industry = {}
for industry, ticker, name in tech_samples:
if industry not in by_industry:
by_industry[industry] = []
by_industry[industry].append((ticker, name))
for industry, stocks in by_industry.items():
print(f"\n {industry} ({len(stocks)} stocks):")
for ticker, name in stocks[:10]: # Show first 10 per industry
print(f" - {ticker:6s} : {name}")
if len(stocks) > 10:
print(f" ... and {len(stocks) - 10} more stocks")
# 4. Check for NULL industries
self.print_separator("4. DATA QUALITY CHECK")
null_count = self.query_null_industries()
if null_count > 0:
print(f"\n ⚠ WARNING: {null_count} stocks have NULL or empty industry field")
else:
print(f"\n ✓ All S&P 500 stocks have industry classification")
# 5. All Industries by Sector (for frontend mapping)
self.print_separator("5. COMPLETE INDUSTRY MAPPING BY SECTOR")
all_industries = self.query_all_industries_by_sector()
for sector, industries in sorted(all_industries.items()):
print(f"\n {sector} ({len(industries)} industries):")
for industry, count in industries:
print(f"{industry:50s} : {count:3d} stocks")
# 6. Export JSON for frontend verification
self.print_separator("6. FRONTEND MAPPING VERIFICATION")
# Create mapping for Technology sector
tech_mapping = {}
for industry, stocks in by_industry.items():
tech_mapping[industry] = {
'count': len(stocks),
'stocks': [{'ticker': t, 'name': n} for t, n in stocks]
}
json_output = {
'technology_sector': {
'total_industries': len(tech_industries),
'total_stocks': tech_total_stocks,
'industries': tech_mapping
},
'all_sectors': {
sector: {
'industry_count': len(industries),
'industries': {ind: cnt for ind, cnt in industries}
}
for sector, industries in all_industries.items()
}
}
output_file = 'sp500_industries_verification.json'
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(json_output, f, indent=2, ensure_ascii=False)
print(f"\n ✓ Detailed mapping exported to: {output_file}")
# 7. Critical Frontend Mapping Issues
self.print_separator("7. CRITICAL ISSUES FOR FRONTEND")
print("\n IMPORTANT: The correct GICS Sector name is:")
print("'Information Technology' (with space)")
print(" ✗ NOT 'TECHNOLOGY' (without space)")
print("\n Frontend code must use exact industry names from database:")
print(" Example industry names:")
for industry, count in tech_industries[:5]:
print(f" - '{industry}'")
print("\n Verify frontend SECTOR_BOXES array and industry mappings use these exact strings.")
except Exception as e:
print(f"\n✗ Analysis failed: {e}")
import traceback
traceback.print_exc()
finally:
self.close()
if __name__ == "__main__":
print("\n" + "=" * 80)
print(" S&P 500 INDUSTRY SUBDIVISION DATABASE VERIFICATION")
print(" Database: 3.38.180.110:8088/if_invest")
print("=" * 80)
analyzer = DatabaseAnalyzer()
analyzer.run_analysis()
print("\n" + "=" * 80)
print(" VERIFICATION COMPLETE")
print("=" * 80 + "\n")

View File

@ -0,0 +1,178 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Verify S&P 500 sector and industry values in PostgreSQL database
"""
import psycopg2
from psycopg2 import sql
from datetime import datetime
# Database connection details
DB_CONFIG = {
'host': '3.38.180.110',
'port': 8088,
'database': 'if_invest',
'user': 'eldsoft',
'password': 'eld240510'
}
def connect_db():
"""Connect to PostgreSQL database"""
try:
conn = psycopg2.connect(**DB_CONFIG)
print(f"[OK] Connected to database: {DB_CONFIG['database']}")
return conn
except Exception as e:
print(f"[ERROR] Database connection failed: {e}")
return None
def execute_query(conn, query, description):
"""Execute query and return results"""
try:
cursor = conn.cursor()
cursor.execute(query)
results = cursor.fetchall()
cursor.close()
return results
except Exception as e:
print(f"[ERROR] Query failed ({description}): {e}")
return None
def main():
print("=" * 80)
print("S&P 500 SECTOR AND INDUSTRY VERIFICATION REPORT")
print(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 80)
print()
conn = connect_db()
if not conn:
return
# Query 1: All unique sector names
print("1. UNIQUE SECTOR NAMES (EXACT DATABASE VALUES)")
print("-" * 80)
query1 = """
SELECT DISTINCT sector
FROM invest_product_code
WHERE is_sp500 = 'Y'
ORDER BY sector;
"""
sectors = execute_query(conn, query1, "unique sectors")
if sectors:
for i, (sector,) in enumerate(sectors, 1):
print(f"{i:2d}. '{sector}'")
print()
# Query 2: Stock count per sector
print("2. STOCK COUNT PER SECTOR")
print("-" * 80)
query2 = """
SELECT sector, COUNT(*) as stock_count
FROM invest_product_code
WHERE is_sp500 = 'Y'
GROUP BY sector
ORDER BY stock_count DESC;
"""
sector_counts = execute_query(conn, query2, "sector counts")
if sector_counts:
total_stocks = 0
print(f"{'Sector Name':<40} {'Stock Count':>10}")
print("-" * 80)
for sector, count in sector_counts:
print(f"{sector:<40} {count:>10}")
total_stocks += count
print("-" * 80)
print(f"{'TOTAL':<40} {total_stocks:>10}")
print()
# Query 3: Industry breakdown per sector
print("3. INDUSTRY BREAKDOWN PER SECTOR")
print("-" * 80)
query3 = """
SELECT sector, industry, COUNT(*) as count
FROM invest_product_code
WHERE is_sp500 = 'Y'
GROUP BY sector, industry
ORDER BY sector, count DESC;
"""
industry_breakdown = execute_query(conn, query3, "industry breakdown")
if industry_breakdown:
current_sector = None
for sector, industry, count in industry_breakdown:
if sector != current_sector:
if current_sector is not None:
print()
print(f"\n[{sector}]")
current_sector = sector
print(f" - {industry}: {count} stocks")
print()
# Query 4: Total S&P 500 stock count
print("4. TOTAL S&P 500 STOCK COUNT")
print("-" * 80)
query4 = """
SELECT COUNT(*) FROM invest_product_code WHERE is_sp500 = 'Y';
"""
total_count = execute_query(conn, query4, "total count")
if total_count:
print(f"Total S&P 500 stocks: {total_count[0][0]}")
print()
# Query 5: Check for NULL or empty sectors
print("5. DATA QUALITY CHECKS")
print("-" * 80)
query5_null = """
SELECT COUNT(*) FROM invest_product_code
WHERE is_sp500 = 'Y' AND (sector IS NULL OR sector = '');
"""
null_sectors = execute_query(conn, query5_null, "null sectors")
if null_sectors:
print(f"Stocks with NULL or empty sector: {null_sectors[0][0]}")
query5_null_industry = """
SELECT COUNT(*) FROM invest_product_code
WHERE is_sp500 = 'Y' AND (industry IS NULL OR industry = '');
"""
null_industries = execute_query(conn, query5_null_industry, "null industries")
if null_industries:
print(f"Stocks with NULL or empty industry: {null_industries[0][0]}")
# Query 6: Sample stocks per sector
print("\n6. SAMPLE STOCKS PER SECTOR (First 3 per sector)")
print("-" * 80)
query6 = """
WITH ranked_stocks AS (
SELECT
sector,
invest_code,
code_desc_en,
industry,
ROW_NUMBER() OVER (PARTITION BY sector ORDER BY invest_code) as rn
FROM invest_product_code
WHERE is_sp500 = 'Y'
)
SELECT sector, invest_code, code_desc_en, industry
FROM ranked_stocks
WHERE rn <= 3
ORDER BY sector, invest_code;
"""
sample_stocks = execute_query(conn, query6, "sample stocks")
if sample_stocks:
current_sector = None
for sector, ticker, name, industry in sample_stocks:
if sector != current_sector:
if current_sector is not None:
print()
print(f"\n[{sector}]")
current_sector = sector
print(f" {ticker:<8} {name:<40} ({industry})")
conn.close()
print("\n" + "=" * 80)
print("VERIFICATION COMPLETE")
print("=" * 80)
if __name__ == "__main__":
main()