国庆假期首日高速公路充电量同比增长 60.4%
Readhub
国庆假期新能源汽车出行热度攀升,今年国庆假期首日全国高速公路充电量达 2804.69 万千瓦时,较去年同期增长 60.40%,创历史节假日单日新高。当前高速公路服务区充电设施运行总体平稳,为新能源汽车长途出行提供补能支撑,下一步国家能源局将会同有关方面继续加强高速公路充电设施监测调度,持续保障假期新能源汽车出行充电需求。
Score: 57.24Confidence: 54%
View offerLoading the catalog…
THE AI OPPORTUNITY INDEX
Find your next AI tool. Explore free access, trials, and credits — all in one place.
Readhub
国庆假期新能源汽车出行热度攀升,今年国庆假期首日全国高速公路充电量达 2804.69 万千瓦时,较去年同期增长 60.40%,创历史节假日单日新高。当前高速公路服务区充电设施运行总体平稳,为新能源汽车长途出行提供补能支撑,下一步国家能源局将会同有关方面继续加强高速公路充电设施监测调度,持续保障假期新能源汽车出行充电需求。
Score: 57.24Confidence: 54%
View offerReadhub
长安汽车 9 月交付 22.89 万辆新车,其中海外交付 96200 辆,同比增长 73.8%。当月交付新能源汽车 113200 辆,同比增长 13.9%。各细分板块中,长安汽车品牌 9 月交付新车 50300 辆,长安启源交付 46507 辆且累计销量突破 100 万辆,深蓝汽车销量达 30185 辆,阿维塔交付 8183 辆,长安凯程交付 19000 辆,其海外交付同比增长 38%。
Score: 57.24Confidence: 54%
View offerReadhub
日本东芝计划 2027 财年内将面向人工智能数据中心的机械硬盘产能提高一倍,拟投资约 600 亿日元扩建菲律宾设施,这是其近五年来首次对 HDD 业务大规模投资,除增设生产线外还将推出单盘存储容量最高提升 40% 的新产品。东芝后续将持续投入资本,目标 2030 年实现 65TB 级硬盘量产,最终达成 100TB 级硬盘量产。当前 AI 数据中心 HDD 存储需求持续扩大,相较于读写速度更快的固态硬盘,HDD 成本仅为其约二十分之一,叠加存储芯片供应紧张进一步推高 HDD 需求。
Score: 57.22Confidence: 54%
View offerReadhub
农业农村部近日印发《全国畜牧兽医行业发展「十五五」规划》,提出包含肉类总产量、禽蛋产量、奶类产量等在内的 15 项定量目标,明确 2030 年我国饲草总产量将达到 1.3 亿吨。
Score: 57.22Confidence: 54%
View offervelog
How to Recover Lost or Misappropriated Funds: A Complete Guide Losing money because of fraud, unauthorized transactions, financial scams, or the misappropriation of funds can be a stressful experience. However, taking the right steps quickly can improve your ability to protect your accounts, document what happened, and explore available recovery options. Whether the loss involves a bank transaction, investment scam, business funds, or another type of financial dispute, understanding the fund recovery process is important. This guide explains what to do after losing money, how lost funds may be recovered, and what steps can help protect you from further financial loss. If you need other information just contact us: Visit Now my websites: https://dqtlx.com Email : info@dqtlx.com How Can Lost or Misappropriated Funds Be Recovered? The first step in recovering lost or misappropriated funds is identifying how the money was lost. The appropriate recovery process can vary depending on whether the loss resulted from fraud, an unauthorized transaction, an accounting error, a financial scam, or another form of misconduct. Start by gathering all available evidence. Keep bank statements, transaction records, invoices, contracts, emails, messages, receipts, and other documents connected to the incident. A complete record can make it easier to explain the situation to your bank, financial institution, authorities, or legal representative. Next, notify the relevant financial institution as soon as possible. If the loss involves an unauthorized transaction, the bank or payment provider may have a specific process for investigating and disputing the transaction. You may also need to report suspected fraud to the appropriate authorities or regulatory organizations. For significant or disputed losses, obtaining advice from a qualified legal professional may help you understand the recovery options available under applicable laws. Acting promptly, preserving evidence, and following the appropriate reporting procedures are important parts of recovering potentially lost funds. What Steps Can Help With Recovering Lost Funds? If you discover that money has been lost or transferred without authorization, taking immediate action can help protect your remaining funds and preserve important evidence. Gather All Relevant Evidence Collect bank statements, payment confirmations, transaction IDs, emails, text messages, contracts, receipts, and other records related to the loss. Avoid deleting communications with the person or organization involved. Contact Your Financial Institution Notify your bank, credit card provider, payment platform, or other relevant financial institution immediately. Explain what happened and ask about their dispute, fraud-reporting, or transaction-reversal procedures. Secure Your Accounts If you suspect that your account information has been compromised, change relevant passwords and enable additional security measures where available. Review recent transactions for other unauthorized activity. Report Suspected Fraud Depending on your location and the nature of the incident, you may need to report the matter to law enforcement, a consumer protection agency, or a financial regulator. Seek Professional Advice When Necessary For substantial losses, complicated financial disputes, or suspected misappropriation, consider consulting an appropriately qualified legal or financial professional. They can help you understand the available options and documentation requirements. Keep Track of Every Report Maintain a record of complaint numbers, correspondence, dates, names of representatives, and actions taken. Organized records can make follow-ups much easier. How Does the Funds Recovery Process Work? The funds recovery process generally depends on how the money was lost and which institution or party handled the transaction. The process often begins by documenting the incident and notifying the relevant financial institution. Banks and payment providers may have internal procedures for investigating disputed or unauthorized transactions. If the initial complaint does not resolve the matter, you may be able to escalate it through the institution's formal complaint process. Depending on the jurisdiction, additional options may include regulatory complaints, consumer protection agencies, mediation, arbitration, or legal proceedings. For cases involving fraud or criminal activity, reporting the incident to law enforcement may also be appropriate. Authorities can determine whether an investigation is warranted. In more complicated cases, a qualified professional may help assess the evidence and identify appropriate legal or financial recovery options. It is important to be cautious when choosing a recovery service. Some scammers specifically target people who have already lost money by promising guaranteed recovery in exchange for upfront fees or sensitive information. What Should You Do After Losing Money to a Financial Scam? If you have lost money to a financial scam, acting quickly is important. Although recovery is not guaranteed, several steps can help limit further damage and preserve potential recovery options. If you need other information just contact us: Visit Now my websites: https://dqtlx.com Email : info@dqtlx.com Contact Your Bank or Payment Provider Immediately Tell the financial institution that you believe you have been the victim of fraud. Provide transaction details and ask whether the payment can be stopped, reversed, or disputed. The available options can depend on the payment method, the timing of the transaction, and the applicable rules. Preserve Evidence Save emails, messages, screenshots, payment confirmations, website addresses, account information, and other communications connected to the scam. Do not alter or delete potentially relevant evidence. Report the Scam Report the incident to the appropriate law enforcement agency, financial regulator, or consumer protection organization in your jurisdiction. Reporting can help authorities identify patterns of fraudulent activity. Protect Your Personal Information If you shared passwords, identification documents, banking information, or other sensitive information with scammers, take steps to secure your accounts. Change compromised passwords and monitor your financial accounts for unusual activity. Be Careful of Follow-Up Recovery Scams After a financial scam, you may be contacted by someone claiming they can recover your money for a fee. Be cautious about unsolicited recovery offers, particularly promises of guaranteed results or requests for upfront payments or sensitive account information. Can Misappropriated Business Funds Be Recovered? Businesses may face fund misappropriation when money is improperly transferred, diverted, or used without authorization. The appropriate response depends on the circumstances and applicable laws. Businesses should preserve financial records, accounting documents, transaction histories, contracts, internal communications, and other relevant evidence. Access to financial systems should also be reviewed and secured where necessary. The business may need to notify its bank, payment provider, auditors, insurers, regulators, or law enforcement, depending on the circumstances. For significant losses, obtaining advice from a qualified legal professional can help determine what recovery or enforcement options may be available. Taking prompt action can also help prevent additional unauthorized transactions while the matter is being investigated. How Can You Protect Yourself From Future Financial Scams? Preventing future losses is an important part of financial security. Consider using strong, unique passwords and enabling multi-factor authentication on financial and email accounts. Regularly review bank and credit card statements and investigate unfamiliar transactions promptly. Be cautious about unexpected investment opportunities, urgent payment requests, and messages asking for passwords, verification codes, or financial information. For businesses, internal controls such as approval procedures, transaction limits, account monitoring, and regular financial reviews can help reduce the risk of unauthorized use of funds. Final Thoughts Recovering lost or misappropriated funds can be complicated, and the available options depend on how the loss occurred, how quickly it was reported, and the laws and procedures that apply. The most important steps are to act promptly, document the incident, notify the relevant financial institution, secure your accounts, and report suspected fraud to the appropriate authorities. For complex or high-value disputes, professional legal or financial advice may also be appropriate. Most importantly, be cautious about anyone who promises guaranteed recovery of your money. Legitimate recovery processes rarely depend on unrealistic promises, and scammers may target victims again after an initial financial loss.
velog
Spring / JPA TIL 1. 오류 해결 Memo 생성자 오류 RequestDto 를 받는 생성자가 없어서 발생. public Memo(RequestDto requestDto) { this.title = requestDto.getTitle(); this.contents = requestDto.getContents(); } IntelliJ: Alt + Insert → Constructor Ctrl + Alt + L → 코드 정렬 Ctrl + Shift + F → 프로젝트 전체 검색 Ctrl + F → 현재 파일 검색 POST 404 오류 Controller와 요청 URL이 달라서 발생. Controller: POST /api/memo 요청: POST /api/memos → API 요청 경로와 Controller의 매핑 경로를 동일하게 맞춰야 함. 2. JDBC JDBC는 Java에서 데이터베이스와 통신하기 위한 표준 API. Java → JDBC → DB Driver → Database 직접 SQL을 작성하고 Connection, Statement, ResultSet 등을 관리해야 함. 3. 3 Layer Architecture Controller → Service → Repository → Database Controller : HTTP 요청/응답 담당 Service : 비즈니스 로직 담당 Repository : DB 접근 담당 4. IoC / DI / Bean IoC 객체의 생성과 관리에 대한 제어권을 개발자가 아닌 Spring이 관리하는 것 . DI 필요한 객체를 직접 생성하지 않고 외부(Spring)에서 주입받는 것 . public MemoService(MemoRepository memoRepository) { this.memoRepository = memoRepository; } 생성자 주입을 주로 사용한다. Bean Spring Container가 생성하고 관리하는 객체. Spring Container → Bean 생성/관리 → 필요한 곳에 DI 5. ORM / JPA ORM Java 객체와 관계형 데이터베이스를 매핑하는 기술. JPA Java ORM을 위한 표준 명세 . Hibernate JPA를 구현한 대표적인 구현체. Spring Data JPA → JPA → Hibernate → JDBC → Database 6. Entity DB 테이블과 매핑되는 Java 객체. @Entity public class Memo { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String title; private String contents; } @Entity → Entity 지정 @Id → PK 지정 @GeneratedValue → ID 자동 생성 DB Row 하나 ↔ Entity 객체 하나 로 이해하면 됨. 7. 영속성 컨텍스트 JPA가 Entity를 관리하는 논리적인 공간. EntityManager → Persistence Context → Entity 관리 주요 기능: 1차 캐시 Dirty Checking Entity 생명주기 관리 Entity 상태 비영속 → 영속 → 준영속 ↘ 삭제 persist() → 영속 detach() → 준영속 clear() → 영속성 컨텍스트 비우기 remove() → 삭제 merge() → 준영속 Entity를 병합 8. Transaction DB 작업을 하나의 작업 단위로 묶는 것. Transaction 시작 → SQL 작업 → 성공 → COMMIT → 실패 → ROLLBACK COMMIT : 변경사항 확정 ROLLBACK : 변경사항 취소 JPA에서는 Transaction 안에서 Entity를 변경하면 Dirty Checking 을 통해 변경사항을 감지하고 DB에 반영할 수 있다. 9. JPA Auditing Entity의 생성/수정 시간을 자동으로 관리하는 기능. @CreatedDate private LocalDateTime createdAt; @LastModifiedDate private LocalDateTime modifiedAt; 10. Query Method Repository 메서드 이름을 기반으로 쿼리를 생성하는 기능. List<Memo> findByTitle(String title); → title 조건으로 조회하는 쿼리가 생성됨. findByTitle findByTitleAndContents findByTitleContaining 등의 규칙을 사용할 수 있다. 11. Java Generics 클래스나 메서드에서 사용할 타입을 지정하는 기능. List<String> names; JPA Repository에서도 사용된다. JpaRepository<Memo, Long> Memo → 관리할 Entity 타입 Long → Entity의 ID 타입 12. 핵심 흐름 Client ↓ Controller ↓ Service ↓ Repository ↓ Spring Data JPA ↓ JPA / Hibernate ↓ JDBC ↓ Database Spring Container → Bean 관리 → DI → Controller / Service / Repository에 객체 주입 Entity → Persistence Context에서 관리 → Dirty Checking → Transaction Commit → DB 반영 한 줄 회고 오늘은 Spring의 기본 구조부터 JPA의 영속성 컨텍스트와 Transaction까지 전체적인 흐름을 연결해서 이해할 수 있었고, 아직 헷갈리는 JPA 동작 원리는 직접 CRUD를 구현하며 더 익혀봐야겠다.
velog
Buy Old Gmail Accounts Buy Are you exploring the digital landscape for a strategic advantage? If so, consider the potential of old Gmail accounts. They may seem like relics from a bygone era, yet they offer unique benefits that modern accounts simply can’t match. As businesses strive to build trust and credibility online, many are turning to these aged accounts as an essential tool in their arsenal. Whether you’re looking to enhance your marketing strategies or protect sensitive information, buying old Gmail accounts could be your next smart move. Dive into this insightful guide and discover why acquiring these vintage assets is becoming increasingly popular among savvy businesses today 👉💪📩📢🔗🤳📶💼➤⚡️🕐 Instant Reply Guaranteed — Message now for quick assistance. 👉💪📩📢🔗🤳📶💼➤Telegram:@getpvasmm 👉💪📩📢🔗🤳📶💼➤WhatsApp: + 1 (830) 739-8217 👉💪📩📢🔗🤳📶💼➤Telegram:@getpvasmm 👉💪📩📢🔗🤳📶💼➤Discord: getpvasmm VISIT OUR SITE: https://getpvasmm.com/product/buy-old-gmail-accounts/ Why people groups buy old Gmail accounts more than modern accounts? Many individuals and organizations gravitate toward Buy old Gmail accounts for their established reputation. These accounts often come with a history that can enhance credibility in online interactions. With age comes trust. Older accounts are less likely to trigger spam filters, making them invaluable for marketing campaigns or important Getpvasmm.com communications. Users feel more secure when engaging with an account that has been around for years. Additionally, seasoned Gmail accounts typically possess higher domain authority. This characteristic can significantly improve email deliverability rates and the success of outreach efforts. For businesses aiming to build relationships quickly, leveraging these aged accounts is a strategic choice. They allow companies to connect authentically while minimizing the risk associated with newer, less-trusted profiles. Investing in Buy old Gmail accounts provides a unique edge—one that modern alternatives cannot replicate easily. Why old Gmail accounts are tallied as culminate for businesses? Buy Old Gmail accounts carry a certain prestige that modern accounts simply cannot match. These aged accounts often have established reputations, making them more trustworthy in the eyes of clients and partners. When businesses utilize older Gmail accounts, they benefit from Getpvasmm.com an already developed history. This can enhance credibility during communications and transactions. Additionally, old accounts are less likely to be flagged for spam activity. Since they’ve been active for a longer time, they come with a level of reliability that newer accounts may lack. Furthermore, many platforms recognize these older profiles as legitimate. This recognition allows businesses to navigate various online landscapes with ease. For companies looking to build relationships or expand their market presence quickly, investing in old Gmail accounts makes strategic sense. Their inherent value lies not just in age but also in the trust they inspire across digital platforms. 👉💪📩📢🔗🤳📶💼➤⚡️🕐 Instant Reply Guaranteed — Message now for quick assistance. 👉💪📩📢🔗🤳📶💼➤Telegram:@getpvasmm 👉💪📩📢🔗🤳📶💼➤WhatsApp: + 1 (830) 739-8217 👉💪📩📢🔗🤳📶💼➤Telegram:@getpvasmm 👉💪📩📢🔗🤳📶💼➤Discord: getpvasmm VISIT OUR SITE: https://getpvasmm.com/product/buy-old-gmail-accounts/ What are the best hones for helpful administration of Gmail accounts? Managing Buy Gmail accounts effectively requires a blend of organization and strategy. First, utilizing labels can help categorize emails by projects or priorities. This makes it easier to locate important messages quickly. Regularly cleaning your inbox is another essential practice. Archiving old emails keeps the inbox clutter-free while still retaining access to previous communications when needed. Automation tools like filters save time by sorting incoming messages automatically. They can direct specific emails straight to designated folders based on criteria you set, enhancing efficiency. Using two-factor authentication adds an extra layer of security, protecting sensitive information from unauthorized access. It’s crucial for safeguarding business communications. Regularly updating passwords ensures account safety as well. Getpvasmm Strong passwords combined with a secure management system reduce risks significantly and contribute to smoother administration of multiple accounts. Why businesses select to buy aged Gmail accounts? Businesses often prefer aged Gmail accounts due to their established credibility. An older account can present a trustworthy image to clients and partners, which is crucial for building relationships. Another factor is the lower risk of getting flagged as spam. New accounts may trigger security algorithms more readily, while older ones have a proven history of legitimate usage. This reliability facilitates smoother communication. Additionally, aged accounts come with access to features that newer accounts may lack at the outset. Features like higher storage limits or advanced settings can enhance workflow efficiency.Many companies also appreciate the chance to acquire verified email addresses that are less likely to face restrictions in various platforms and services. This helps streamline operations across multiple channels without unnecessary interruptions. 👉💪📩📢🔗🤳📶💼➤⚡️🕐 Instant Reply Guaranteed — Message now for quick assistance. 👉💪📩📢🔗🤳📶💼➤Telegram:@getpvasmm 👉💪📩📢🔗🤳📶💼➤WhatsApp: + 1 (830) 739-8217 👉💪📩📢🔗🤳📶💼➤Telegram:@getpvasmm 👉💪📩📢🔗🤳📶💼➤Discord: getpvasmm VISIT OUR SITE: https://getpvasmm.com/product/buy-old-gmail-accounts/ How can old Gmail accounts ensure client data and delicate data? Buy Old Gmail accounts often come with established trust and credibility. They have a history that can provide a sense of reliability for clients. A well-aged account generally has fewer spam filters, ensuring smoother communication. Security is another significant factor. Older accounts usually have robust security measures in place, like two-factor authentication, which helps protect sensitive information. This added layer keeps client data safer from breaches and unauthorized access. Moreover, seasoned accounts typically set up better recovery options. If an issue arises, accessing the account becomes easier due to prior recovery settings and linked devices.Clients also feel more at ease communicating through established addresses rather than newly created ones that might raise suspicion or concern about legitimacy. The longer history means less likelihood of being flagged as spam by other email systems too.Let’s attempt to investigate the genuine esteem of old Gmail accounts- Getpvasmm.comBuy Old Gmail accounts carry a unique value that often goes unnoticed. They possess a history that new accounts simply can’t replicate. This history fosters trust, especially in professional settings.These aged accounts typically boast higher credibility. Businesses appreciate this when reaching out to clients or partners. An established account is seen as more trustworthy than one created yesterday. Why we propose businesses to Buy Old Gmail accounts in bulk? Buy old Gmail accounts in bulk presents significant advantages for businesses. First, these accounts often come with established credibility. They have a history that can enhance trustworthiness when reaching out to clients or partners. Moreover, older accounts are less likely to be flagged as spam. This means higher deliverability rates for emails sent from these addresses, ensuring important communications reach their intended recipients without hassle. Additionally, bulk purchases allow companies to save time and resources. Instead of creating new accounts from Getpvasmm scratch, they can quickly acquire multiple aged profiles ready for use. When scaling operations or launching marketing campaigns, having access numerous stable email addresses streamlines processes. It provides flexibility in managing outreach and communication strategies effectively without the worry of account limitations common with newer profiles. 👉💪📩📢🔗🤳📶💼➤⚡️🕐 Instant Reply Guaranteed — Message now for quick assistance. 👉💪📩📢🔗🤳📶💼➤Telegram:@getpvasmm 👉💪📩📢🔗🤳📶💼➤WhatsApp: + 1 (830) 739-8217 👉💪📩📢🔗🤳📶💼➤Telegram:@getpvasmm 👉💪📩📢🔗🤳📶💼➤Discord: getpvasmm VISIT OUR SITE: https://getpvasmm.com/product/buy-old-gmail-accounts/ What are the best integrative of aged Gmail accounts? Buy Aged Gmail accounts come with several advantageous integrations that can enhance business productivity. One notable integration is the seamless connection to Google Workspace tools, like Docs and Sheets. This allows teams to collaborate effortlessly in real-time. Another strong point is the compatibility with various third-party applications. Many businesses rely on CRM systems or marketing platforms that easily link with Gmail, streamlining communication and data management. The ability to set up custom filters and labels also adds value. Users can organize incoming emails efficiently, ensuring critical messages don’t get lost in clutter. Additionally, older accounts often have a higher trust score among email providers, which may lead to better deliverability rates for campaigns. This reliability makes them an attractive option for businesses aiming for consistent outreach efforts without being flagged as spam. Utilizing aged accounts opens doors to improved analytics too. Businesses can track interactions more effectively over time, refining their strategies accordingly. Buy Old Gmail Accounts Are you exploring the digital landscape for a strategic advantage? Getpvasmm.com If so, consider the potential of old Gmail accounts. They may seem like relics from a bygone era, yet they offer unique benefits that modern accounts simply can’t match. As businesses strive to build trust and credibility online, many are turning to the
velog
JavaScript_Day.9 Day.8의 콜백 함수 다음 단계, Promise와 async/await. "비동기 작업이 끝난 뒤의 결과를 어떻게 받을까"에 대한 두 번째, 세 번째 답을 배운 날이다. 학습 아홉 번째 날이다. 오늘 배운 내용은 이렇다. Promise : 상태(pending, fulfilled, rejected), resolve 와 reject then , catch 와 체이닝 async/await 와 try/catch 콜백, Promise, async/await 비교 이벤트 버블링과 stopPropagation || , && 로 값 고르기 (리액트 문법) map , forEach , filter 1. Promise Promise는 비동기 작업(서버 요청, 타이머 등)의 결과를 나중에 받아서 처리하기 위해 쓰는 객체다. 상태 관리가 명확하고, then 을 이어 붙이는 체이닝 문법 이라 코드 읽기가 편하다. const promise = new Promise((resolve, reject) => { // 성공: resolve(data) → 보통 성공했을 때 호출 // 실패: reject(error) → 실패했을 때 호출 }); new Promise() 의 인자로 비동기 작업을 진행할 콜백 함수를 넣는다. 이 함수를 executor 라고 하고, 매개변수로 받는 resolve 와 reject 가 Promise의 상태를 바꾸는 스위치다. Promise의 세 가지 상태 상태 의미 pending 대기 중. resolve 도 reject 도 아직 호출되지 않은 초기 상태 fulfilled 성공. resolve 가 호출됨 rejected 실패. reject 가 호출됨 pending 상태 확인하기 const promise = new Promise(() => { setTimeout(() => { console.log('hi'); }, 3000); }); console.log(promise); // Promise {<pending>} // (3초 뒤) hi resolve 와 reject 를 받지도, 호출하지도 않았으므로 이 Promise는 영원히 pending 이다. 3초 뒤 hi 가 출력되지만, Promise의 상태는 바뀌지 않는다. resolve로 fulfilled 만들기 const promise = new Promise((resolve, reject) => { setTimeout(() => { console.log('hi'); resolve('안녕'); // 성공 상태로 바꾸고, 결과값으로 '안녕'을 전달 }, 3000); }); setTimeout(() => { console.log(promise); }, 3000); // hi // Promise {<fulfilled>: '안녕'} 두 타이머가 모두 3초지만, Promise 안의 타이머가 먼저 등록되어 있어서 먼저 실행된다. resolve('안녕') 이 호출되어 상태가 fulfilled 가 되고, 그 뒤에 아래 타이머가 상태를 출력한다. reject로 rejected 만들기 const promise = new Promise((resolve, reject) => { setTimeout(() => { console.log('hi'); reject('reject'); // 실패 상태로 바꾸고, 실패 이유로 'reject'를 전달 }, 3000); }); setTimeout(() => { console.log(promise); }, 3000); // hi // Promise {<rejected>: 'reject'} reject("reject") 는 단순히 값을 반환하는 게 아니라, reject 스위치를 작동시키면서 그 값을 실패 이유로 전달 하는 것이다. 이 코드를 콘솔에서 실행하면 Uncaught (in promise) reject 라는 빨간 오류도 함께 뜬다. 실패한 Promise를 catch 로 처리하지 않았기 때문이다. 바로 아래에서 catch 를 다룬다. 조건에 따라 성공과 실패 나누기 const promise = new Promise((resolve, reject) => { setTimeout(() => { const n = 100; if (typeof n === 'number') { resolve(n + 100); // 숫자이면 성공: 200 } else { reject('너 숫자 아닌데'); // 숫자가 아니면 실패 } }, 3000); }); setTimeout(() => { console.log(promise); // Promise {<fulfilled>: 200} console.log(typeof(promise)); // object }, 4000); Promise는 3초가 걸리는 작업 이고, 확인하는 타이머는 4초다. 3초에 resolve(200) 이 호출되어 이미 fulfilled 가 된 뒤에 출력하므로 성공으로 보인다. 만약 확인하는 타이머가 2초였다면 아직 작업이 끝나기 전이라 pending 으로 보였을 것이다. typeof(promise) 는 object 다. Promise도 객체이다. 2. then과 catch 상태를 직접 출력해 보는 방식은 불편하다. 보통은 성공했을 때 실행할 함수 와 실패했을 때 실행할 함수 를 등록한다. then: 성공했을 때 const promise = new Promise((resolve, reject) => { setTimeout(() => { const n = 100; if (typeof n === 'number') { resolve(n + 100); } else { reject('너 숫자 아닌데'); } }, 3000); }); promise.then((value) => { console.log(value); // 3초 뒤 200 }); then 은 "그 후에"라는 뜻이다. Promise가 성공했을 때 실행할 콜백을 등록하는 메소드다. executor에서 resolve 를 호출하면, then 에 전달한 콜백이 실행되면서 resolve 에 넘긴 값이 매개변수 value 로 들어온다. 실행 순서를 정리하면 이렇다. new Promise 가 실행되고 executor가 실행되어 setTimeout 이 예약된다. (3초 대기) promise.then(...) 이 등록되어 resolve 나 reject 를 기다린다. 3초 뒤 setTimeout 이 실행된다. n 이 숫자이므로 resolve(200) 이 호출된다. Promise가 pending 에서 fulfilled 로 바뀐다. 등록해 둔 then() 이 실행되면서 value 로 200을 받아 출력한다. 필기에서는 이 예제의 n 을 '100' (문자열)으로 적어 놓고 결과를 200으로 설명했는데, '100' 은 typeof 가 string 이라서 reject로 가고 then 은 실행되지 않는다. 위 코드는 숫자 100 으로 고쳤다. catch: 실패했을 때 const promise = new Promise((resolve, reject) => { setTimeout(() => { const n = null; if (typeof n === 'number') { resolve(n + 100); } else { reject('너 숫자 아닌데'); } }, 3000); }); promise.catch((error) => { console.log(error); // 3초 뒤 '너 숫자 아닌데' }); catch 는 then 과 반대로 Promise의 reject, 즉 실패값을 받아 실행되는 함수 를 등록한다. reject 에 전달한 값이 error 로 들어온다. null 의 typeof 는 'object' 라서 숫자 조건을 통과하지 못하고 reject로 간다. 3. 체이닝 then 을 이어 붙여서 앞 단계의 결과를 다음 단계로 넘길 수 있다. 각 then 이 return 한 값이 다음 then 의 value 가 된다. const myPromise = new Promise((resolve) => resolve('foo')); myPromise .then((value) => `${value} and bar`) .then((value) => `${value} and bar again`) .then((value) => `${value} and again`) .then((value) => `${value} and again`) .then((value) => { console.log(value); // foo and bar and bar again and again and again }) .catch((err) => { console.error(err); // 중간에 어디서든 실패하면 여기로 }); 위에서 아래로 읽으면 순서대로 일이 진행되는 것이 보인다. 중간 어느 단계에서 문제가 생기든 맨 끝의 catch 하나가 처리한다. Day.8의 콜백 함수로 같은 일을 하려면 콜백 안에 콜백을 계속 넣어야 해서 코드가 안쪽으로 파고드는데, 체이닝은 이를 평평하게 펴 준다. 4. async / await 필기 제목에 별표가 잔뜩 붙은 부분이다. "초 중요!" async 함수는 항상 Promise를 반환한다 async function test() { return { name: 'gildong', id: 'hong' }; } console.log(test()); // Promise {<fulfilled>: {name: 'gildong', id: 'hong'}} 함수 앞에 async 를 붙이면 그 함수의 반환값을 자동으로 Promise로 감싸 준다. 위 함수는 기다릴 코드가 없고 즉시 끝까지 실행되므로, 바로 fulfilled 상태의 Promise가 반환된다. 안에서 시간이 걸리는 경우 async function test() { return new Promise((resolve, reject) => { setTimeout(() => { resolve({ name: 'gildong', id: 'hong' }); }, 2000); }); } console.log(test()); // Promise {<pending>} test().then(result => console.log(result)); // 2초 뒤 {name: 'gildong', id: 'hong'} console.log(test()) 는 Promise 객체 자체 를 출력한 것이다. 내부 Promise가 아직 기다리는 중이라 pending 으로 보인다. .then(result => ...) 의 result 는 resolve 에 넘긴 객체 값 이다. 2초 뒤 fulfilled 가 되면서 값이 나온다. await: 끝날 때까지 기다린다 async function test() { await new Promise((resolve) => setTimeout(resolve, 2000)); // 그냥 2초 동안 기다리는 장치다 return { name: 'aa', age: '123' }; } async function test2() { const data = await test(); // test()가 끝날 때까지 기다렸다가 결과를 data에 대입 console.log(data); } test2(); // 2초 뒤 {name: 'aa', age: '123'} await 는 Promise가 끝날 때까지(결과값을 받을 때까지) 기다린다. setTimeout(resolve, 2000) 은 "2초 뒤에 resolve 를 호출해라"라는 뜻이다. 값 없이 시간만 보내는 용도로 쓸 수 있다. async 가 필요한 경우는 두 가지다. 1 함수 안에서 await 를 쓸 때, 2 함수가 Promise를 반환하게 만들고 싶을 때. await 는 async 함수 안에서만 쓸 수 있다. 그래서 위의 test2 도 async 로 선언했다. function test() { return new Promise((resolve) => { setTimeout(() => { resolve('1초 후 완료'); }, 1000); }); } async function start() { console.log('시작'); const result = await test(); // Promise가 끝날 때까지 기다린 뒤, resolve로 전달된 문자열을 result에 저장 console.log(result); console.log('끝'); } start(); // 시작 // (1초 뒤) 1초 후 완료 // 끝 await 덕분에 비동기 코드인데도 위에서 아래로 순서대로 읽힌다. console.log('끝') 은 await 가 끝나기 전까지 실행되지 않는다. 5. try / catch로 실패 처리 체이닝에서는 성공이면 then , 실패이면 catch 였다. async/await에서는 성공이면 try 안, 실패(reject)이면 catch 안 으로 간다. console.log('홍길동'); function profile(id) { return new Promise((resolve, reject) => { setTimeout(() => { if (id == 1) { resolve({ id: 1, name: '김길동' }); } else { reject('프로필 없음'); } }, 1000); }); } console.log('이길동'); 여기까지의 출력은 홍길동 , 이길동 이다. profile() 을 호출하기 전이라 동기 코드만 실행된다. 체이닝 방식 id 를 100으로 주면 reject 가 호출되므로 catch 가 실행된다. profile(100) .then((user) => console.log(user)) .catch((error) => console.log(error)); // 홍길동 // 이길동 // (1초 뒤) 프로필 없음 profile(1) 이면 then 이 실행되어 {id: 1, name: '김길동'} 이 출력된다. async/await 방식 async function start() { try { // 예외가 날 수도 있는 코드 const pro = await profile(100); console.log(pro); } catch (error) { // 예외가 발생하면 여기서 처리 console.log(error); // 프로필 없음 } } start(); 방식 성공 실패 체이닝 .then() .catch() async/await try { } catch (error) { } await 한 Promise가 reject되면 그 지점에서 예외가 던져지고, catch 블록으로 넘어간다. 6. 콜백, Promise, async/await 비교 같은 일을 세 가지 방식으로 구현해 보면 차이가 보인다. 3초 뒤 'hi' 를 전달받아 출력하는 문제다. 1 콜백 function fun1(callback) { setTimeout(() => { callback('hi'); }, 3000); } fun1((msg) => { console.log(msg); }); 2 Promise function fun2() { return new Promise((resolve, reject) => { setTimeout(() => resolve('hi'), 3000); }); } fun2().then((value) => console.log(value)); 3 async/await async function start() { const msg = await fun2(); console.log(msg); } start(); 방식 결과를 받는 법 특징 콜백 함수를 인자로 넘기고, 완료 시점에 호출 단계가 많아지면 콜백 안에 콜백이 겹친다 Promise then , catch 로 연결 체이닝으로 평평하게 쓸 수 있다 async/await await 로 기다려서 변수에 받음 동기 코드처럼 위에서 아래로 읽힌다 커피 예제(Day.8의 coffee )도 같은 방식으로 바꿔 본다. // Promise를 반환하는 함수 function test() { return new Promise((resolve) => { setTimeout(() => { resolve('latte'); }, 2000); }); } async function coffee() { let coffee2 = await test(); console.log(coffee2); // 2초 뒤 latte } coffee(); 필기의 test 는 async function test() 로 되어 있었는데, 이미 new Promise 를 직접 반환하고 있으므로 async 를 붙이지 않아도 결과가 같다. 위에서는 뺐다. 수업 문제에서 필기에 "내가 푼 것"과 "강사님이 푼 것"을 나란히 적어 놓은 부분이 있다. // 내가 푼 것 const fun1 = new Promise((resolve, reject) => { setTimeout(() => resolve('hi'), 3000); }); fun1.then((value) => console.log(value)); // 강사님이 푼 것 function fun2() { return new Promise((resolve, reject) => { setTimeout(() => resolve('hi'), 3000); }); } 둘 다 hi 가 나오지만 차이가 있다. const fun1 = new Promise(...) 은 변수에 대입하는 순간 작업이 시작 되고, 호출할 때마다 새로 실행되는 것이 아니다. fun2() 처럼 함수로 감싸면 호출하는 시점에 Promise가 새로 만들어져 필요할 때 필요한 만큼 실행할 수 있다. async/await로 바꿀 때도 await fun2() 처럼 함수 호출 형태가 자연스럽다. 7. 이벤트 버블링 <style> #d1 { border: 3px solid black; padding: 20px; } #d1 a { text-decoration: none; border: 3px solid black; } </style> <div id="d1"> <a class="a1" href="#none">태그링크</a> </div> <script> let d1 = document.querySelector('#d1'); let a1 = document.querySelector('.a1'); // div를 클릭하면 테두리 색을 blue로 (고전 이벤트 방식) d1.onclick = function () { this.style.borderColor = 'blue'; }; // a1을 클릭하면 red a1.addEventListener('click', function (e) { e.stopPropagation(); // 전파 막기 this.style.borderColor = 'red'; }); </script> stopPropagation 줄을 지우고 a 를 클릭하면 a 의 테두리는 빨강, 부모 div 의 테두리도 파랑으로 바뀐다. 처음에는 a 만 클릭했는데 왜 부모까지 바뀌는지가 의문이었다. 답은 이벤트 버블링 이다. 자식 요소에서 이벤트가 발생하면 그 이벤트가 부모, 그 위 부모로 거품처럼 올라가며 전달된다. a 는 d1 의 자식이고 d1 에 클릭 이벤트가 걸려 있으므로, a 를 클릭하면 d1 의 onclick 도 함께 실행된다. e.stopPropagation() : 이벤트 전파를 막는다. a 를 클릭해도 부모 d1 의 이벤트가 실행되지 않는다. 반대로 위에서 아래로 내려가는 단계를 캡처링 이라고 한다. 이벤트는 캡처링 → 대상 → 버블링 순서로 흐르는데, 보통 쓰는 것은 버블링이다. Day.8의 To Do List에서 목록 전체( #list )에만 이벤트를 걸어도 안쪽 아이콘 클릭이 처리되었던 이유가 바로 이 버블링 덕분이다. 자식의 이벤트가 부모까지 올라오니까 부모에서 e.target 으로 받을 수 있었다. 8. || 와 && 로 값 고르기 (리액트 문법) || 와 && 는 Day.2에서 조건식에 쓰는 논리 연산자로 배웠다. 리액트에서는 이것을 조건에 따라 값을 고르는 용도 로 쓴다. falsy 값 아래 값들은 조건에서 모두 false로 인식 된다. false 0 "" (빈 문자열) null undefined NaN || 연산자 왼쪽이 true 로 판정되면 → 왼쪽 값 이 반환된다. 왼쪽이 false 로 판정되면 → 오른쪽 값 이 반환된다. const n = null; const m = n || '문자열'; console.log(m); // 문자열 (왼쪽이 falsy → 오른쪽) const n2 = 20; const m2 = n2 || '문자열'; console.log(
velog
Spring MVC부터 SQL/JDBC까지 정리 1. MVC 디자인 패턴 소프트웨어를 Model, View, Controller 로 구분하여 각각의 역할을 분리하는 디자인 패턴이다. Model : 데이터와 비즈니스 로직을 담당하며, 데이터베이스와 연동하여 데이터를 저장하고 조회한다. View : 사용자 인터페이스를 담당하며, 사용자가 보는 화면과 버튼, 폼 등을 구현한다. Controller : Model과 View 사이의 상호작용을 조정한다. 사용자의 입력을 받아 Model에 전달하고, Model의 결과를 바탕으로 View를 업데이트한다. MVC의 장점 코드 재사용성 유지보수성 개발자 간 협업 용이 2. Spring MVC Spring MVC는 Servlet을 기반으로 HTTP 요청을 처리하고, MVC 디자인 패턴을 적용해 요청과 응답을 효율적으로 관리한다. Servlet Java를 사용하여 웹 페이지를 동적으로 생성하는 서버 측 프로그램 또는 사양이다. Servlet 동작 과정 Client(브라우저)에서 HTTP Request 발생 HttpServletRequest , HttpServletResponse 객체 생성 요청에 해당하는 Servlet 탐색 service() 호출 → HTTP Method에 따라 doGet() , doPost() 등 호출 처리 결과를 HttpServletResponse 에 담아 Client에 반환 응답 완료 후 Request / Response 객체 소멸 DispatcherServlet DispatcherServlet 은 요청을 분석하고 적절한 Controller로 요청을 전달한다. 예를 들어 @GetMapping("/api/hello") 요청이 들어오면 해당 요청을 처리할 Controller의 메서드를 찾아 실행한다. 동작 과정 HTTP 요청 분석 Handler Mapping 을 통해 Controller에 요청 전달 Controller에서 처리 후 결과(Model, View) 전달 DispatcherServlet 이 View에 Model을 적용하여 Client에 전달 3. @Controller @Controller 는 관련된 요청을 하나의 Controller에서 관리할 수 있도록 한다. 예를 들어 회원 관리 기능의 로그인, 회원가입, 로그아웃 등을 하나의 Controller에서 관리할 수 있다. @Controller @RequestMapping("/user") public class UserController { @GetMapping("/login") public String login() { // ... } @GetMapping("/logout") public String logout() { // ... } @GetMapping("/signup") public String signup() { // ... } @PostMapping("/signup") public String registerUser(SignupRequestDto requestDto) { // ... } } URL 경로 중복 HTTP Method가 다르면 같은 URL 경로를 사용할 수 있다. @GetMapping("/api/hello") @ResponseBody public String hello() { return "Hello World"; } @PostMapping("/api/hello") @ResponseBody public String post() { return "Hello World2"; } 4. @RequestMapping 공통되는 API 경로를 묶어서 관리할 수 있다. @Controller @RequestMapping("/api") public class HelloController { @GetMapping("/hello") @ResponseBody public String hello() { return "Hello World"; } } 위와 같이 설정하면 실제 요청 경로는 /api/hello 가 된다. 5. 정적 페이지와 동적 페이지 정적(Static) 페이지 이미 완성된 HTML 파일을 그대로 반환하는 페이지다. 예를 들어 http://localhost:8080/hello.html 을 요청하면 Controller를 거치지 않고 정적 리소스를 반환할 수 있다. Redirect @GetMapping("/html/redirect") public String htmlStatic() { return "redirect:/hello.html"; } redirect: 를 사용하면 해당 URL로 다시 요청을 보낸다. 동적 페이지 Controller에서 Model에 데이터를 담고, Template Engine이 HTML에 데이터를 적용하여 동적인 페이지를 생성한다. @GetMapping("/html/dynamic") public String htmlDynamic(Model model) { visitCount++; model.addAttribute("visits", visitCount); return "hello-visit"; } model.addAttribute() 로 전달한 값은 HTML의 ${visits} 와 같은 표현식에 적용된다. 동적 페이지 처리 과정 Controller에서 요청 처리 및 필요한 데이터를 Model에 저장 Template Engine(Thymeleaf)에 View와 Model 전달 Template Engine이 View에 Model을 적용 생성된 HTML을 Client에 전달 대표적인 Template Engine으로 Thymeleaf, Groovy, FreeMarker, JSP 등이 있다. 6. JSON 데이터 반환 서버에서 HTML 전체를 반환하는 대신 필요한 데이터를 JSON 형식으로 반환할 수 있다. String 반환 @GetMapping("/response/json/string") @ResponseBody public String helloStringJson() { return "{\"name\":\"Robbie\",\"age\":95}"; } Java 객체 반환 @GetMapping("/response/json/class") @ResponseBody public Star helloClassJson() { return new Star("Robbie", 95); } Spring은 Java 객체를 JSON으로 변환하여 반환할 수 있다. 7. @RestController @RestController 는 @Controller 와 @ResponseBody 를 함께 사용하는 것과 같다. @RestController public class HelloController { // ... } @ResponseBody 를 사용하면 HTML View가 아닌 데이터를 응답으로 반환한다. 8. Jackson Jackson은 JSON 데이터를 처리하는 라이브러리다. Java Object → JSON JSON → Java Object Spring은 Jackson과 관련된 기능을 제공하여 Java 객체를 JSON으로 변환할 수 있다. @Test @DisplayName("Object To JSON") void test1() throws JsonProcessingException { Star star = new Star("Robbie", 95); ObjectMapper objectMapper = new ObjectMapper(); String json = objectMapper.writeValueAsString(star); System.out.println("json = " + json); } 9. Path Variable과 Request Param 둘 다 HTTP 요청으로 데이터를 전달받는 방법이지만, 데이터가 URL에 표현되는 방식이 다르다. Path Variable URL 경로에 값을 포함하여 전달하는 방식이다. // GET http://localhost:8080/hello/request/star/Robbie/age/95 @GetMapping("/star/{name}/age/{age}") @ResponseBody public String helloRequestPath( @PathVariable String name, @PathVariable int age ) { return String.format( "Hello, @PathVariable.<br> name = %s, age = %d", name, age ); } {name} , {age} 형태로 URL 경로에 변수 지정 @PathVariable 로 해당 값 전달받음 Request Param Query String을 통해 데이터를 전달하는 방식이다. // GET http://localhost:8080/hello/request/form/param?name=Robbie&age=95 @GetMapping("/form/param") @ResponseBody public String helloGetRequestParam( @RequestParam(required = false) String name, int age ) { return String.format( "Hello, @RequestParam.<br> name = %s, age = %d", name, age ); } @RequestParam 생략 가능 required = false 설정 시 해당 값이 없어도 됨 10. HTTP 데이터를 객체로 처리 @ModelAttribute 여러 데이터를 하나씩 @RequestParam 으로 받는 대신 Java 객체로 한 번에 받을 수 있다. // POST http://localhost:8080/hello/request/form/model // Content-Type: application/x-www-form-urlencoded // Body: name=Robbie&age=95 @PostMapping("/form/model") @ResponseBody public String helloRequestBodyForm(@ModelAttribute Star star) { return String.format( "Hello, @ModelAttribute.<br> (name = %s, age = %d)", star.name, star.age ); } Query String에서도 사용할 수 있다. // GET http://localhost:8080/hello/request/form/param/model?name=Robbie&age=95 @GetMapping("/form/param/model") @ResponseBody public String helloRequestParam(@ModelAttribute Star star) { return String.format( "Hello, @ModelAttribute.<br> (name = %s, age = %d)", star.name, star.age ); } @ModelAttribute 는 생략할 수 있다. @RequestBody JSON 형식으로 전달된 HTTP Body를 Java 객체로 받을 때 사용한다. // POST http://localhost:8080/hello/request/form/json // Content-Type: application/json // Body: {"name":"Robbie","age":"95"} @PostMapping("/form/json") @ResponseBody public String helloPostRequestJson(@RequestBody Star star) { return String.format( "Hello, @RequestBody.<br> (name = %s, age = %d)", star.name, star.age ); } 11. CRUD 구현 CRUD는 데이터를 생성(Create), 조회(Read), 수정(Update), 삭제(Delete)하는 기본적인 데이터 처리 방식이다. DTO란? DTO(Data Transfer Object)는 데이터 전송 및 이동을 위해 사용하는 객체다. Client에서 전달받은 데이터를 객체로 처리 서버 계층 간 데이터 이동 Create @PostMapping("/memos") public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto) { // RequestDto -> Entity Memo memo = new Memo(requestDto); // Memo Max ID Check Long maxId = memoList.size() > 0 ? Collections.max(memoList.keySet()) + 1 : 1; memo.setId(maxId); // DB 저장 memoList.put(memo.getId(), memo); // Entity -> ResponseDto MemoResponseDto memoResponseDto = new MemoResponseDto(memo); return memoResponseDto; } Read @GetMapping("/memos") public List<MemoResponseDto> getMemos() { List<MemoResponseDto> responseList = memoList.values().stream() .map(MemoResponseDto::new) .toList(); return responseList; } Update @PutMapping("/memos/{id}") public Long updateMemo( @PathVariable Long id, @RequestBody MemoRequestDto requestDto ) { if (memoList.containsKey(id)) { Memo memo = memoList.get(id); memo.update(requestDto); return memo.getId(); } else { throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다."); } } Delete @DeleteMapping("/memos/{id}") public Long deleteMemo(@PathVariable Long id) { if (memoList.containsKey(id)) { memoList.remove(id); return id; } else { throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다."); } } 12. Database Spring에서 데이터를 다루기 위해서는 애플리케이션뿐 아니라 데이터베이스의 기본 구조도 이해해야 한다. DB(Database) : 데이터의 집합 DBMS(Database Management System) : 데이터베이스를 관리하고 운영하는 소프트웨어 RDBMS(Relational DBMS) : 관계형 데이터베이스 관리 시스템 RDBMS는 테이블 을 기본 단위로 데이터를 관리한다. 테이블은 행(row)과 열(column)으로 구성된다. 테이블 간에는 외래키를 통해 관계를 구성할 수 있다. 아이디 이름 전화번호 1 홍길동 010-7777-7777 2 김철수 010-6666-6666 3 이가영 010-8888-8888 13. SQL SQL(Structured Query Language)은 RDBMS에서 데이터를 조작하고 관리하기 위해 사용하는 언어다. DDL DDL(Data Definition Language)은 데이터베이스와 테이블의 구조를 정의하는 언어 CREATE CREATE DATABASE 데이터베이스이름; CREATE TABLE 테이블이름 ( 필드이름1 필드타입1, 필드이름2 필드타입2 ); ALTER ALTER TABLE 테이블이름 ADD 필드이름 필드타입; ALTER TABLE 테이블이름 DROP 필드이름; ALTER TABLE 테이블이름 MODIFY COLUMN 필드이름 필드타입; DROP 데이터베이스 또는 테이블을 삭제한다. DROP DATABASE 데이터베이스이름; DROP TABLE 테이블이름; TRUNCATE 테이블의 데이터를 삭제하고 테이블 구조는 유지한다. TRUNCATE TABLE 테이블이름; DML DML(Data Manipulation Language)은 데이터를 조작하는 언어 INSERT INSERT INTO 테이블이름(필드이름1, 필드이름2, 필드이름3) VALUES(데이터값1, 데이터값2, 데이터값3); SELECT SELECT 필드이름 FROM 테이블이름 WHERE 조건; UPDATE UPDATE 테이블이름 SET 필드이름1 = 데이터값1, 필드이름2 = 데이터값2 WHERE 필드이름 = 데이터값; DELETE DELETE FROM 테이블이름 WHERE 필드이름 = 데이터값; DCL DCL(Data Control Language)은 데이터베이스의 권한을 제어하는 언어 GRANT 사용자 또는 ROLE에 권한을 부여한다. GRANT [객체권한명] (컬럼) ON [객체명] TO { 유저명 | 롤명 | PUBLIC } [WITH GRANT OPTION]; REVOKE 사용자 또는 ROLE에 부여한 권한을 회수한다. REVOKE { 권한명 [, 권한명...] | ALL } ON 객체명 FROM { 유저명 [, 유저명...] | 롤명 | PUBLIC }; 14. JDBC JDBC(Java Database Connectivity)는 Java에서 데이터베이스에 접근할 수 있도록 제공하는 API다. JDBC를 사용하면 애플리케이션의 DB 접근 로직을 특정 DB에 직접 종속시키는 것을 줄일 수 있다. 애플리케이션의 DB 접근 과정 DB 연결(Connection) SQL 요청 결과 응답 예를 들어 MySQL에서 PostgreSQL로 DB를 변경할 경우 DB 연결 및 요청 방식이 달라질 수 있다. JDBC는 이러한 DB 접근을 위한 표준화된 인터페이스를 제공한다. JDBC Template JDBC를 사용할 때 반복되는 작업을 줄이기 위해 Spring에서는 JdbcTemplate 을 제공한다. 커넥션 연결 Statement 준비 및 실행 커넥션 종료 반복 작업을 줄여줌 등의 반복적인 작업을 대신 처리할 수 있다. 마무리 DispatcherServlet 을 중심으로 HTTP 요청이 Controller까지 전달되는 흐름 @RestController , @ResponseBody 를 이용한 JSON 응답 @PathVariable , @RequestParam , @ModelAttribute , @RequestBody 의 차이 CRUD의 기본적인 데이터 처리 방식 DDL / DML / DCL의 역할 JDBC와 JdbcTemplate 의 역할
velog
집에서 직접 개발한 웹 서버를 친구에게 보여주려 하거나, 로컬에서 구동한 게임 서버, SSH 원격 접속, 홈 NAS 등을 외부에 개방할 때 항상 마주치는 필수 관문이 바로 포트 포워딩(Port Forwarding) 이다. 공유기(사설망) 환경에서 왜 외부 접속이 곧바로 불가능한지, 포트 포워딩이 어떤 원리로 외부 요청을 내 컴퓨터로 정확히 배달해 주는지 정리한다. 1. 문제 상황: "분명 서버를 켰는데, 밖에서 왜 접속이 안 될까?" 내 방 컴퓨터에서 포트 8080 으로 웹 서버를 실행한 뒤, 같은 집 와이파이를 쓰는 노트북이나 스마트폰에서 192.168.0.5:8080 으로 접속하면 페이지가 잘 열린다. 하지만 회사나 카페, 혹은 친구 집 컴퓨터에서 내 주소로 접속을 시도하면 "사이트에 연결할 수 없음(Connection Timed Out)" 오류가 발생한다. 왜 바로 접속할 수 없을까? 이유는 내 컴퓨터가 공유기 뒤편의 '사설 IP(Private IP)'망 에 숨어 있기 때문이다. 사설 IP는 인터넷에서 길을 찾을 수 없다 내 컴퓨터의 IP( 192.168.0.5 )는 전 세계 수많은 가정과 사무실에서도 똑같이 사용 중인 내부 식별 번호다. 공인 인터넷망(백본 라우터)은 사설 IP 목적지 패킷을 받는 즉시 폐기(Drop)하도록 규정되어 있다. 공유기의 공인 IP로 접속해도 공유기는 누구 것인지 모른다 친구가 우리 집 공유기의 공인 IP( 203.0.113.50 ) 를 알아내어 접속 요청을 보냈다고 가정하자. 공유기 아래에는 내 데스크톱( 192.168.0.5 ), 거실 TV( 192.168.0.2 ), 스마트폰( 192.168.0.3 ) 등 여러 기기가 물려 있다. 외부에서 불쑥 먼저 들어온 접속 요청(Inbound)에 대해 공유기는 "이 8080번 데이터가 데스크톱 것인지, 스마트폰 것인지" 판단할 근거가 전혀 없다. 결국 공유기는 보안과 오류 방지를 위해 패킷을 버린다. [외부 접속자(친구)] [공유기 (공인 IP: 203.0.113.50)] [내부 사설망 기기들] | | | --- "접속 요청합니다!" (Port 8080) ---> | | | -- ? (누구에게 줘야 할지 모름) | | -- 데스크톱(192.168.0.5)? | | -- 스마트폰(192.168.0.2)? | | => 패킷 폐기 (접속 실패) 2. 포트 포워딩(Port Forwarding)이란? 포트 포워딩은 공유기에게 "외부에서 공유기의 특정 문(Port)으로 패킷이 들어오면, 내부에 있는 특정 사설 IP의 컴퓨터로 곧장 넘겨줘라" 하고 정적인 이정표(규칙)를 미리 지정해 두는 기술이다. 포트(Port) : 컴퓨터 안에서 동작하는 수많은 프로그램(웹 서버, 게임, 메신저 등) 중 어느 서비스로 갈지 지정하는 '방 번호' 포워딩(Forwarding) : 패킷을 가로채서 다른 곳으로 '전달(전송)' 쉽게 비유하자면, 회사 대표 번호(공인 IP)로 전화를 걸었을 때 교환원에게 "내선 번호 8080번(외부 포트)을 누르면 기획팀 홍길동의 자리(내부 사설 IP:포트)로 즉시 연결해 주세요"라고 시스템을 세팅해 두는 것과 같다. 3. 포트 포워딩의 동작 원리 포트 포워딩 규칙을 등록한 뒤 실제 외부 사용자의 접속 요청이 처리되는 단계별 과정이다. 3.1 공유기 관리자 설정 예시 외부 포트(External Port) : 8080 (외부 접속자가 입력할 포트) 내부 IP(Internal IP) : 192.168.0.5 (내 방 컴퓨터의 사설 IP) 내부 포트(Internal Port) : 8080 (내 방 컴퓨터에서 실제 실행 중인 프로그램의 포트) 프로토콜 : TCP 3.2 패킷 이동 흐름 [외부 접속자] [공유기 (포트 포워딩 규칙 적용)] [내 컴퓨터 (192.168.0.5)] | | | | -- 1. 203.0.113.50:8080 접속 ------> | | | | (규칙 확인: 8080번은 192.168.0.5로!) | | | -- 2. 목적지를 192.168.0.5로 변환 -> | | | (DNAT: 목적지 주소 변경) | | | | | <-- 4. 응답 전송 ------------------- | <-- 3. 응답 데이터 회신 ----------- | 외부 요청 : 친구가 브라우저에 203.0.113.50:8080 을 입력하고 엔터를 누른다. 규칙 대조 및 변환 : 공유기가 요청 패킷을 수신하고 포트 번호( 8080 )를 확인한다. 사전에 정의된 포트 포워딩 규칙을 찾아 목적지 IP를 공유기 공인 IP에서 192.168.0.5:8080 으로 바꿔치기(DNAT: Destination NAT) 한다. 내부 전달 : 변환된 패킷이 로컬 스위치를 거쳐 내 컴퓨터의 웹 서버 프로세스에 정상 도달한다. 응답 회신 : 내 컴퓨터가 웹페이지 데이터를 응답하면, 공유기는 다시 출발지 주소를 자신의 공인 IP로 변경하여 친구에게 전송한다. 4. 포트 포워딩 설정 시 필수 체크리스트 3가지 공유기 설정 페이지(보통 192.168.0.1 등)에서 포트 포워딩을 설정했음에도 여전히 접속되지 않는다면 다음 3가지를 반드시 확인해야 한다. 1) 내 컴퓨터의 사설 IP를 '고정'했는가? 기본적으로 공유기는 DHCP를 통해 기기들에게 사설 IP를 유동적으로 대여해 준다. 컴퓨터를 껐다 켜거나 공유기가 재부팅되면 내 컴퓨터의 IP가 192.168.0.5 에서 192.168.0.8 로 바뀔 수 있다. IP가 바뀌면 포트 포워딩 규칙(구 IP를 가리킴)이 어긋나므로, 공유기의 [DHCP 고정 IP 할당] 메뉴에서 내 컴퓨터의 MAC 주소와 사설 IP를 묶어두어야 한다. 2) 내 컴퓨터의 로컬 방화벽(Windows Defender 등)이 열려 있는가? 공유기가 패킷을 내 컴퓨터 앞까지 무사히 배달해 주어도, Windows 방화벽이 해당 포트의 외부 인바운드 접속을 차단하면 통신이 되지 않는다. Windows Defender 고급 보안 방화벽의 [인바운드 규칙] 에서 내가 사용하는 포트(예: TCP 8080)를 허용해 주어야 한다. 3) 통신사 공유기가 2중으로 물려 있지 않은가? (이중 NAT / Double NAT) 원룸이나 아파트 환경에서는 통신사 모뎀 겸 공유기(1차) 아래에 본인의 공유기(2차)를 추가로 연결해 쓰는 경우가 흔하다. 이 경우 최상단 통신사 공유기에서도 2차 공유기로 포트 포워딩을 해주거나, 2차 공유기를 브리지 모드(AP 모드) 로 전환해야 정상 통신이 가능하다. 5. 핵심 요약 구분 일반적인 웹서핑 (NAT) 포트 포워딩 (Port Forwarding) 통신 시작점 내부 기기 $\rightarrow$ 외부 인터넷 외부 사용자 $\rightarrow$ 내부 기기 변환 방식 출발지 주소를 공인 IP로 변환 (SNAT) 목적지 주소를 특정 내부 사설 IP로 변환 (DNAT) 테이블 성격 요청 시 동적으로 생성되었다가 통신 종료 시 소멸 관리자가 수동으로 입력해 둔 영구적인 정적 규칙 주 사용처 브라우저 서핑, 유튜브 시청, 파일 다운로드 웹 서버 호스팅, SSH 원격 접속, 게임 서버 오픈 사설 IP는 외부 인터넷에서 직접 찾을 수 없으므로, 외부 접속자는 공유기의 공인 IP 로 접근해야 한다. 공유기는 외부에서 먼저 들어온 요청을 어느 내부 컴퓨터로 줘야 할지 모르기 때문에 포트 포워딩 규칙(정적 매핑) 이 필요하다. 포트 포워딩을 안전하게 운용하려면 내부 IP 고정 과 로컬 OS 방화벽 인바운드 허용 이 함께 이루어져야 한다.
velog
오늘의 명언 : A discovery is said to be an accident meeting a prepared mind. _ Albert Szent-Gyorgyi "발견은 준비된 사람이 맞닥뜨린 우연이다." _ 알베르트 센트죄르지 오늘은 새로운 챕터인 전처리 및 시각화 파트에 들어갔다. 사실 가장 기대하고 또 열심히 해야겠다고 생각한 파트이기도 하다.. 최선을 다해서 달려보자~! 데이터 전처리 및 시각화 Day 1 오늘부터 새로운 과정인 데이터 전처리 및 시각화 를 시작했다. 그런데 첫날이라 그런지 본격적으로 Pandas를 이용해서 데이터를 만진다기보다는 데이터 분석은 왜 하는가? 데이터 전처리는 왜 필요한가? 앞으로 무엇을 배우게 되는가? 를 먼저 알아보는 오리엔테이션에 가까운 수업이었다. 그래서 오늘은 강의 내용 자체는 가볍게 정리하고 코드카타에서 새롭게 배운 부분을 조금 더 자세하게 기록해보려고 한다. 1. 데이터 분석은 왜 할까? 처음에는 데이터 분석이라고 하면 단순히 데이터를 계산하고 정리하는 일이라고 생각하기 쉬운데 오늘 강의에서는 데이터의 목적을 설득 이라고 표현했다. 데이터를 분석하는 것 자체가 목적이 아니라 어떤 문제가 있는지 확인하고 데이터를 통해 근거를 만들고 그 결과를 다른 사람에게 전달하고 의사결정을 할 수 있도록 만드는 것 까지가 중요하다는 의미였다. 특히 데이터가 아무리 많아도 전달하려는 내용이 명확하지 않으면 의미가 없기 때문에 시각화 역시 단순히 그래프를 예쁘게 만드는 기술이 아니라 분석 결과를 빠르게 이해시키기 위한 방법이라고 볼 수 있었다. 2. 전처리부터 무작정 시작하면 안 된다 오늘 가장 기억에 남았던 부분이다. 데이터를 받자마자 바로 가공하는 것이 아니라 먼저 목표 무엇을 확인하려고 데이터를 분석하는가? 예상 산출물 최종적으로 어떤 결과를 만들 것인가? As-is 현재 어떤 문제가 있는가? To-be 분석을 통해 무엇을 개선하고 싶은가? 를 생각해야 한다. 예를 들어 품질 데이터가 있다고 하면 단순히 "불량 데이터를 정리해야겠다" 가 아니라 "최근 불량률이 증가한 원인을 확인하기 위해 생산라인별 불량률을 비교할 수 있도록 데이터를 가공한다." 처럼 목적부터 정하는 것이 더 중요하다는 것이다. 품질관리에서도 꽤 중요한 사고방식이라고 느꼈다. 3. 데이터 전처리란? 데이터 전처리는 쉽게 말해서 내가 원하는 데이터를 보기 위해 데이터를 가공하는 모든 과정 이라고 이해했다. 실제 데이터는 생각보다 깔끔하지 않다. 예를 들어 성별 데이터가 남 남성 여 여자 여성 처럼 작성되어 있을 수도 있고 전화번호도 010-1234-5678 01012345678 010-12345678 처럼 형식이 제각각일 수 있다. 이런 데이터를 분석하려면 먼저 같은 의미를 가진 데이터들을 동일한 형식으로 맞춰야 한다. 품질 데이터에서도 비슷할 것 같다. 치수불량 치수 불량 치수NG Dimension NG 같은 데이터가 있다면 그대로 분석하기보다 하나의 기준으로 통일하는 작업이 먼저 필요할 것이다. 앞으로 이런 작업을 Pandas를 이용해서 배우게 된다. 4. Excel과 Pandas Excel도 데이터를 다루는 데 굉장히 좋은 도구지만 Pandas는 Python 코드로 데이터를 처리하기 때문에 반복적인 작업을 자동화하기 좋다는 장점이 있다. 특히 앞으로 배우게 될 작업은 데이터 정렬 데이터 필터링 집계 결측값 처리 데이터 가공 시각화 등이다. 오늘은 깊게 들어가지는 않았고 Pandas에서 사용하는 데이터 구조를 간단히 살펴봤다. DataFrame 표 형태의 데이터 Excel의 표를 생각하면 이해하기 쉽다. 제품 생산량 불량수 0 A 100 3 1 B 120 5 2 C 90 2 Series DataFrame에서 하나의 열을 가져온 형태라고 생각하면 된다. 예를 들어 생산량 100 120 90 하나의 속성을 가진 데이터 집합이다. 아직은 개념만 알아두고 앞으로 실제 Pandas 코드를 사용하면서 익숙해지면 될 것 같다. 5. 오늘의 코드카타 오늘도 Python 코드카타를 진행했다. 오늘은 특히 문자열 처리와 zip() 그리고 반복문을 이용한 누적 계산 문제가 많이 나왔다. 6. 수박수박수박수... 길이 n 에 맞게 수박수박수박... 패턴을 만드는 문제였다. 처음에는 이런 아이디어를 전혀 생각하지 못했다. 가장 간단한 방법은 def solution(n): return ("수박" * n)[:n] 이었다. 예를 들어 "수박" * 3 을 하면 수박수박수박 이 되고 [:3] 으로 앞의 세 글자만 가져오면 수박수 가 된다. 문자열도 숫자처럼 * 를 이용해서 반복할 수 있다는 것을 다시 확인했다. 하지만 지금 나한테 더 이해하기 쉬운 정석 풀이는 아래 방식이었다. def solution(n): answer = "" for i in range(n): if i % 2 == 0: answer += "수" else: answer += "박" return answer 인덱스가 0 1 2 3 4 ... 로 진행되기 때문에 짝수 → 수 홀수 → 박 을 넣는 방식이다. 오늘의 포인트 같은 문제라도 반복문으로 직접 구현하는 방법 문자열의 규칙을 이용해서 간단하게 만드는 방법 둘 다 존재할 수 있다. 아직은 정석적인 반복문 풀이를 먼저 이해하고 짧은 풀이는 새로운 아이디어로 챙겨가는 게 좋은 것 같다. 7. 두 배열의 내적과 zip() 두 리스트의 같은 위치에 있는 숫자를 곱한 뒤 모두 더하는 문제였다. a = [1, 2, 3] b = [4, 5, 6] 이라면 1 × 4 2 × 5 3 × 6 을 모두 더하면 된다. 인덱스를 이용하면 def solution(a, b): answer = 0 for i in range(len(a)): answer += a[i] * b[i] return answer 으로 해결할 수 있다. 그런데 이전에도 한 번 배웠던 zip() 을 사용하면 더 직관적으로 표현할 수 있었다. def solution(a, b): answer = 0 for x, y in zip(a, b): answer += x * y return answer zip(a, b) 는 같은 위치의 값을 묶어준다. a = [1, 2, 3] b = [4, 5, 6] 이면 (1, 4) (2, 5) (3, 6) 처럼 짝을 만들어준다. 그래서 for x, y in zip(a, b): 라고 하면 각각의 값을 바로 사용할 수 있다. 기억하기 zip() = 여러 리스트의 같은 위치 값을 묶어준다. 개인적으로 range(len()) 보다 zip() 방식이 더 직관적으로 느껴졌다. 8. 약수의 개수가 짝수인지 홀수인지 left 부터 right 까지의 숫자 중 약수의 개수가 짝수 → 더하기 약수의 개수가 홀수 → 빼기 를 하는 문제였다. 우선 약수를 직접 세는 방식으로 풀 수 있다. def solution(left, right): answer = 0 for n in range(left, right + 1): count = 0 for i in range(1, n + 1): if n % i == 0: count += 1 if count % 2 == 0: answer += n else: answer -= n return answer 여기서는 반복문이 두 번 사용된다. 첫 번째 반복문은 for n in range(left, right + 1): 각 숫자를 확인하기 위한 것이고 두 번째 반복문은 for i in range(1, n + 1): 해당 숫자의 약수를 찾기 위한 것이다. 그리고 if n % i == 0: 이면 나누어떨어지는 것이므로 i 가 약수라는 뜻이다. 추가로 새로운 규칙도 알게 되었다. 제곱수는 약수의 개수가 홀수이다. 예를 들어 16 은 1 × 16 2 × 8 4 × 4 에서 4 × 4 처럼 같은 숫자가 짝을 이루기 때문에 약수의 개수가 홀수가 된다. 그래서 제곱수인지 확인하는 방식으로도 문제를 풀 수 있다. if int(n ** 0.5) ** 2 == n: 예전에 완전제곱수 문제에서 봤던 코드가 다시 등장했다. 9. 문자열 내림차순 정렬 문자열을 큰 문자부터 작은 문자 순으로 정렬하는 문제였다. 처음부터 한 줄로 작성하기보다 단계별로 풀면 def solution(s): answer = list(s) answer.sort(reverse=True) answer = "".join(answer) return answer 이 된다. 흐름은 문자열 ↓ list() ↓ sort(reverse=True) ↓ join() ↓ 문자열 이다. 예를 들어 s = "Zbcdefg" 를 list(s) 하면 ['Z', 'b', 'c', 'd', 'e', 'f', 'g'] 가 되고 answer.sort(reverse=True) 하면 ['g', 'f', 'e', 'd', 'c', 'b', 'Z'] 가 된다. 마지막으로 "".join(answer) 을 사용하면 gfedcbZ 라는 문자열이 된다. 예전에 숫자를 내림차순으로 정렬했던 문제와 거의 같은 구조였다. 이번에는 처음부터 문자열이기 때문에 마지막에 다시 int() 로 변환할 필요가 없다는 차이가 있다. 10. 놀이기구 이용료 계산 놀이기구를 탈 때마다 가격이 1번째 → price × 1 2번째 → price × 2 3번째 → price × 3 ... 으로 증가하는 문제였다. 정석적으로 풀면 def solution(price, money, count): total = 0 for i in range(1, count + 1): total += price * i if total > money: return total - money else: return 0 으로 해결할 수 있다. 예를 들어 price = 3 money = 20 count = 4 라면 3 × 1 = 3 3 × 2 = 6 3 × 3 = 9 3 × 4 = 12 총 이용료는 3 + 6 + 9 + 12 = 30 이 되고 가진 돈이 20이므로 30 - 20 = 10 이 부족하다. 이 문제는 내가 지금까지 많이 연습했던 answer = 0 for ... answer += ... if ... 형태의 누적 계산 문제라 이전보다 훨씬 익숙하게 느껴졌다. 11. 오늘 새롭게 익힌 것 오늘 코드카타를 하면서 다시 느낀 것은 문제를 보고 바로 짧은 코드를 떠올리지 못해도 괜찮다는 것이다. 일단 내가 알고 있는 for if range % list sort join 같은 문법으로 하나씩 구현할 수 있으면 된다. 그다음 다른 사람의 풀이를 보면서 ("수박" * n)[:n] 처럼 문자열의 특성을 이용하거나 zip(a, b) 처럼 기존 반복문을 더 직관적으로 표현하는 방법을 하나씩 추가하면 된다. 특히 오늘 기억할 것은 zip() → 같은 위치의 값 묶기 "문자열" * n → 문자열 반복 [:n] → 앞에서 n개까지 자르기 sort(reverse=True) → 내림차순 정렬 "".join(list) → 문자 리스트를 문자열로 합치기 n % i == 0 → 약수인지 확인 answer += 값 → 반복하면서 값 누적 이다. 12. Day 1 마무리 오늘 데이터 전처리 및 시각화 수업은 본격적인 실습보다는 앞으로 무엇을 배우게 될지 방향을 잡는 시간에 가까웠다. 그래도 분석 목적 설정 → 필요한 데이터 전처리 → 분석 → 시각화 → 의사결정 이라는 전체적인 흐름을 먼저 이해할 수 있었다. 다음 수업부터 Pandas를 본격적으로 사용하게 되면 오늘 배운 왜 데이터를 전처리하는가? 라는 질문을 계속 생각하면서 코드를 작성해봐야겠다. 코드카타에서는 점점 새로운 문법 자체보다 기존에 배운 문법을 어떻게 조합해서 문제를 해결할지가 중요해지고 있는 것 같다. 특히 오늘은 zip() 을 다시 사용해본 것과 문자열 반복 + 슬라이싱처럼 내가 쉽게 떠올리지 못했던 풀이를 본 것이 가장 기억에 남았다. 이.해.완.료
velog
华纳国际万宝路华纳直营放心娱乐。 东南亚最靠谱的综合平台 提款秒到,大额无忧 ♣️百+乐♥️,牛牛,龙🐉虎🐅,炸金花,推同子,PG🎮AG,麻将胡🀄️,捕鱼,彩票,体育 上百款电子游戏等你来体验你暴富我暴单双赢才是我们的目标 不会注册或注册不了的请联系下方代理专业进行开户即可游戏 出入支持。银行卡,微信,支付宝,USDT,康讯,缅帀 万宝路官方网址: 228983.com PG电子网址: sts2956.com 代理专员: @stswt01 招商代理: @stswt01 代理QQ: 3874447568
Score: 54.4Confidence: 49%
View offerScore: 54.4Confidence: 49%
Score: 54.4Confidence: 49%
Score: 54.4Confidence: 49%
Score: 54.4Confidence: 49%
Score: 54.4Confidence: 49%
Score: 54.4Confidence: 49%
Score: 54.4Confidence: 49%