묻고 답해요
160만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결재고시스템으로 알아보는 동시성이슈 해결방법
단일연산
- 학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요! - 먼저 유사한 질문이 있었는지 검색해보세요. - 서로 예의를 지키며 존중하는 문화를 만들어가요. - 잠깐! 인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요. final Stock stock = stockRepository.getByProductId(productId); stock.decrease(quantity); stockRepository.save(stock) public void decrease(final Long quantity) { if (this.quantity - quantity < 0) { throw new RuntimeException("재고 부족"); } this.quantity -= quantity; } 락을 건 이유는 decrease 로직에서 quantity를 줄이고 다시 저장했기 때문이라 생각했습니다. stock 상태가 코드레벨에서 변하기 때문에 발생하기 때문입니다.다만 코드레벨에서 변경하지 않고 디비에서 직접 변경한다면(update stock quantity=quantity-1 과 같이) update 자체가 배타락이기 때문에 굳이 락을 걸지 않아도 된다고 판단하였습니다. 그래서 단일 연산으로 해결할 수 없는 상황은 quantity 필드 뿐만 아니라 다른 필드에도 영향이 미칠 때라 생각했습니다. stock 필드에 soft delete 필드 delYn을 추가하고,코드 상에서 만약 재고가 0이면 delYn=True 로 바꾼다 이런 경우일 것 같은데 제가 생각한게 맞을까요? 단일 연산으로도 정합성을 유지할 수 있는데 왜 stock을 다시 저장하는지 모르겠습니다
-
미해결[유니티 레벨 업!] 모듈식으로 개발하는 아이템 시스템
NPOI 오류가 뜨는데
프로젝트 다운받아서 빌드 해보니 Assets\Scripts\Test\NPOIExample.cs(2,7): error CS0246: The type or namespace name 'NPOI' could not be found (are you missing a using directive or an assembly reference?)이런오류들이 발생하는데 혹시 원인이 뭘까요? 제미나이에 물어보니 NPOI 관련해서 설정을 이것저것 해줬는데도 해결이 안되네요.. Assets\Scripts\Test\ExcelConvertTest.cs(656,45): error CS0246: The type or namespace name 'IRow' could not be found (are you missing a using directive or an assembly reference?)이런오류들도 발생을 하네요 ㅜㅜ
-
미해결스프링 MVC 2편 - 백엔드 웹 개발 활용 기술
DB 국제화에 대해서 궁금합니다.
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오) 예2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예[질문 내용]예전에 국제화 강의도 들었습니다.고정값은 properties를 통하여 국제화를 한다고 알고 있습니다. 현재 프로젝트에서 국제화를 진행중인데고정값같은 경우는 프론트에서 국제화를 진행하기로 하였고 게시글같은 경우는 따로 번역을 하여서 백엔드에서 들고있기로 하였는데그렇게되면 기존 DB 구조를 바꿔야할것같아서요.어떻게 보통 실무에서 많이 하시는지 궁금합니다.인터넷에서는 정보가 없어서요.예시같은것을 들어주시면 이해가 좀 될거같습니다. 게시글 테이블로 title, content가 국제화가 된다고 하면 어떻게 해야할까요 ... 왜 그 방법을 사용하는지도 알려주시면 감사하겠습니다.그리고 영한님께서 답변하신 연관된 링크를 보았는데 이해가 가지 않는것도 있습니다.https://inf.run/MSJjn
-
미해결[코드팩토리] [초급] Flutter 3.0 앱 개발 - 10개의 프로젝트로 오늘 초보 탈출!
설명이 너무 헷갈립니다.
왜 자꾸 안가르쳐주신 부분을 당연한 듯이 쓰시는 지 모르겠습니다…ㅜㅜ 그 전전 강의까지만해도 color를 red blue로 변경하는 부분을 다 if문을 사용하여 작성하셨는데 갑자기 show =show == true ? false : true 를 설명 안하시고 사용하시는데 너무 헷갈립니다. 솔직히 다트 언어 배울때도 니중에 설명하신다고 하고 나중에는 다시 이전에 배웠죠 이런식으로 하시는데 너무 헷갈립니다.
-
해결됨AI와 함께 배우는 Next.js
Next.js 시작하기 강의는 어떻게 무료로 받을수있나요??
강의 시작에 앞서 Next.js 시작하기 강의를 먼저 듣고 시작하고 싶은데 무료로 어떻게 받을수있을까요?
-
미해결처음하는 딥러닝과 파이토치(Pytorch) 부트캠프 (쉽게! 기본부터 챗GPT 핵심 트랜스포머까지) [데이터분석/과학 Part3]
강의 필기는 어떻게 하나요?
강의 자료에 필기는 어떻게 하나요? 그냥 보기만 하는 용도인가요
-
미해결
SAP E_ACTAI_2403 Exam 점수 향상 방법
SAP E_ACTAI_2403 시험을 준비 중이신가요? 이 가이드는 성과를 향상시키는 데 도움이 될 수 있는 검증된 전략과 팁을 안내합니다. SAP에 대해 처음 접하는 분이든 경험이 풍부한 전문가든, 이 단계들을 따르면 시험 결과에서 상당한 차이를 만들 수 있습니다.시험 구조 이해하기SAP E_ACTAI_2403 시험 점수를 향상시키기 위한 첫 번째 단계는 시험의 구조를 이해하는 것입니다. 이 시험은 SAP AI, 머신 러닝, 다양한 비즈니스 프로세스와의 통합과 같은 핵심 분야에 중점을 둡니다. 문제는 실용적인 지식을 테스트하기 위해 설계되었으므로 시험 형식에 익숙해지는 것이 중요합니다.학습 계획 만들기SAP E_ACTAI_2403 시험을 준비하는 가장 효과적인 방법 중 하나는 상세한 학습 계획을 세우는 것입니다. 학습 시간을 관리 가능한 작은 단위로 나누고 한 번에 한 주제에 집중하세요. 각 학습 세션에 구체적인 목표를 설정하면 더 조직적이고 동기 부여를 받을 수 있습니다. 또한, SAP E_ACTAI_2403 Exam Questions 풀어보면 어떤 유형의 문제가 나올지 이해하는 데 도움이 되고, 더 효과적으로 준비할 수 있습니다.고급 학습 자료 사용하기SAP E_ACTAI_2403 시험에 합격할 가능성을 높이려면 고급 학습 자료를 사용하는 것이 중요합니다. 책, 온라인 강좌, 연습 시험은 시험 내용을 이해하는 데 필요한 필수 리소스입니다. 실제 시험과 유사한 문제를 풀어보는 것이 중요합니다. 문제를 풀어보는 것은 형식에 익숙해지고 자신의 지식을 테스트하는 데 도움이 됩니다. CertBoosters와 같은 플랫폼은 실제 시험 문제를 반영하는 연습 테스트를 제공하여 자신감을 높이는 데 도움이 됩니다.핵심 주제에 집중하기시험 전체 내용을 공부하는 것이 유혹적일 수 있지만, 핵심 주제에 집중하는 것이 준비를 더 효율적으로 만들 수 있습니다. 가장 중요한 개념을 검토하고 그것들이 SAP의 응용 프로그램 맥락에서 어떻게 연결되는지 이해하도록 하세요. 여기에는 머신 러닝 알고리즘, 예측 분석, 비즈니스 프로세스 최적화와 같은 영역이 포함됩니다.일관성 있게 학습하고 휴식 취하기어떤 시험을 준비할 때 일관성이 핵심입니다. 학습 일정을 지키고 매일 학습을 습관화하세요. 하지만 학습 중에는 꼭 휴식을 취하세요. 짧은 휴식은 뇌가 정보를 흡수하고 탈진을 방지하는 데 도움이 됩니다. 휴식 시간에는 긴장을 풀고 재충전할 수 있도록 하세요. 이렇게 하면 학습한 내용을 압도당하지 않고 소화할 수 있습니다.시험 당일 차분하고 자신감 있게SAP E_ACTAI_2403 시험 당일에는 차분하고 자신감을 가지세요. 준비에 대한 자신감을 가지고 시험 중에 자신을 의심하지 마세요. 각 문제를 신중하게 읽고, 시험 내내 시간을 잘 배분하세요. 중요한 것은 단지 내용을 아는 것이 아니라, 제한된 시간 내에 그 지식을 얼마나 효과적으로 적용할 수 있는가입니다.결론SAP E_ACTAI_2403 시험 점수를 향상시키려면 헌신, 계획, 그리고 적합한 학습 자료가 필요합니다. 이 전략들을 따르면—시험 구조 이해하기, 학습 계획 만들기, 고급 학습 자료 사용하기, 핵심 주제에 집중하기, 모의 시험 연습하기, 일관성 있게 학습하기, 스트레스 관리하기—시험 준비를 더 잘 할 수 있습니다. 준비 잘 하시길 바랍니다!
-
해결됨[퇴근후딴짓] 빅데이터 분석기사 실기 (작업형1,2,3)
안녕하세요 ! listbox = [""감사", "행복", "사랑", "습관", "긍정", "변화", "성장", "공감", "희망"]에서 마지막 앞 단어의 코드를 적을때 그냥 listbox[-2]라고 표현하면 안되나요?
학습 관련 질문을 남겨주세요. 상세히 작성하면 더 좋아요!질문과 관련된 영상 위치를 알려주면 더 빠르게 답변할 수 있어요먼저 유사한 질문이 있었는지 검색해보세요
-
미해결[개정판] 파이썬 머신러닝 완벽 가이드
파이썬 다운그레이 후 사이킷런 재설치
안녕하세요, 강의 잘 보고 있습니다.강의 영상에 설명해주신 대로 사이킷런을 1.0.2 버전을 설치하려고 했으나, 주피터 노트북에선 1.6.2버전으로 나와 해결 방법을 찾아보던 중 파이썬을 다운 3.9버전으로 다운 그레이드하면 된다는 답변을 받았습니다. 다만, 파이썬을 다운 그레이드 후 사이킷런을 다시 설치하면 주피터 노트북에서 sklearn이라는 모듈이 없다는 오류가 발생합니다.. 혹시 아나콘다 프롬프트를 관리자 권한으로 실행 후 파이썬을 다운 그레이드 하고, 사이킷런을 1.0.2 버전으로 설치할 수 있는 방법을 알 수 있을까요?
-
미해결개발자에게 필요한 로그 관리
안녕하세요 혹시 multipart request요청일때는 파라미터가 손실 되시지 않는가요??
컨틀롤러 단에서 데이터가 유실되는것 같습니다.
-
미해결RAG를 활용한 LLM Application 개발 (feat. LangChain)
mac으로 hugging face 양자화 공유합니다.(질문도 있습니다)
안녕하세요.강의에서 나오는 BitsAndBytesConfig를 통한 양자화 예시에서 CUDA가 필수로 요구되어 macbook에서 진행할 수 있는 방안을 찾다가 mlx_lm을 통해서 MPS(mac의 gpu)를 활용하여 양자화가 가능하단 사실을 알게되어 공유드리고자 합니다. 아마 맥북 사용자도 꽤 많을 것으로 예상됩니다. langchain에서 MLXPipeline을 제공하지만 이를 사용했을 때 현재 invoke시 호출되는 _call 메서드 내부에서 generate_step이 mlx_lm에서 제공은 하지만 MLXPipeline에서 제대로 불러올 수 없어 RunnableLambda를 통해서 에러를 우회해봤습니다.(Q? 강사님은 혹시 해결 방법을 알고 계실까요? 공홈 코드가 다 안 되네요 ㅜㅜ 오류 코드는 맨 아래 첨부하겠습니다.) 먼저 양자화 진행하는 방식입니다. cli를 통한 command 또는 python-api를 활용하는 방법 두 가지가 있습니다. 자세한건 링크 첨부하겠습니다. 양자화 개념에 대해서 자세히 나오니 참고하면 좋으실 듯 합니다.링크: [quantization with xlm_lm](https://developer.apple.com/kr/videos/play/wwdc2025/298/?time=187) mlx_lm을 통한 양자화먼저 양자화한 model을 local path에 저장합니다. 그 전에 apple의 gpu를 확인할 수 있는 코드 부분은 처음 부분에서 확인하실 수 있습니다.사실 python코드를 싸는 것보다 command로 양자화 진행하는게 편한 것 같습니다. 전 projection layer와 embedding layer는 다른 layer보다 높은 bit로 해주는게 좋다고 하여 python으로 진행했습니다. dequantize on-the-fly(게산 추론)시 더 좋다고 합니다. # mac에서 mps를 사용한 예제 import torch from mlx_lm.convert import convert print("MPS available on this device =>", torch.backends.mps.is_available()) # projection layer & embedding layer는 6bit, 양자화 가능한 layer는 4bit, 양자화 불가능은 False return def mixed_quantization(layer_path, layer): if "lm_head" in layer_path or 'embed_tokens' in layer_path: return {"bits": 6, "group_size": 64} elif hasattr(layer, "to_quantized"): return {"bits": 4, "group_size": 64} else: return False # quantization 진행 convert( hf_path="microsoft/Phi-3-mini-4k-instruct", mlx_path="./models/microsoft-Phi-3-mini-4k-instruct-mixed-4-6-bit", dtype="float16", quantize=True, q_bits=4, q_group_size=64, quant_predicate=mixed_quantization )$) mlx_lm.convert --hf-path "mistralai/Mistral-7B-Instruct-v0.3" \ --mlx-path "./mistral-7b-v0.3-4bit" \ --dtype float16 \ --quantize --q-bits 4 --q-group-size 64 --upload-repo "my-name/mistral-7b-v0.3-4bit" Langchain과 연계하기 from functools import wraps from langchain_core.runnables import RunnableLambda from langchain_core.output_parsers import StrOutputParser from mlx_lm import generate, load quantized_model_path = "./models/microsoft-Phi-3-mini-4k-instruct-mixed-4-6-bit" model, tokenizer = load(quantized_model_path) def runnable_wrapper(func): """RunnableLambda wrapper function""" @wraps(func) def wrapper(*args, **kwargs): return RunnableLambda(func) return wrapper @runnable_wrapper def create_chat_prompt(question): messages = [ { "role": "system", "content": """ You are an expert in information retrieval and summarization. Your job is to read the provided text and produce a precise, faithful, and concise summary. Prioritize the author’s main claim, key evidence, and conclusions. Use plain English and avoid filler. Do not invent facts that aren’t present in the input. """ }, { "role": "user", "content": f""" Question: {question} """ } ] prompt_without_tokenized = tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=False) prompt_with_tokenized = tokenizer.apply_chat_template(messages, add_generation_prompt=True) print("생성된 prompt👇\n", prompt_without_tokenized) return prompt_with_tokenized @runnable_wrapper def run_llm_with_mlx(prompt): return generate(model=model, tokenizer=tokenizer, prompt=prompt) @runnable_wrapper def output_parser(answer): return answer.replace("<|end|>", "") chain = create_chat_prompt() | run_llm_with_mlx() | output_parser()오류 코드llm.invoke에서 에러가 발생하며 TypeError: generate_step() got an unexpected keyword argument 'formatter'라는 에러가 발생합니다.from langchain_community.llms.mlx_pipeline import MLXPipeline from mlx_lm import load model_path = "./models/microsoft-Phi-3-mini-4k-instruct-mixed-4-6-bit" model, tokenizer = load(model_path) llm = MLXPipeline( model=model, tokenizer=tokenizer ) llm.invoke("what's my name?")
-
해결됨CS 지식의 정석 | 디자인패턴 네트워크 운영체제 데이터베이스 자료구조
컨텍스트는 context와 contextual information으로 나눠진다는게 무슨뜻인가요?
안녕하세요 강의 잘듣고있습니다.교안에 컨텍스트는 context와 contextual information으로 나눠진다고 서술되어 있는데 information이 context안에 포함된 구조가 아니라 나눠진 개념인가요?
-
미해결React Native with Expo: 제로초에게 제대로 배우기
안드로이드 애니메이션 관련 질문
// app.config.ts const config: ExpoConfig = { .... android: { adaptiveIcon: { backgroundColor: '#000000', foregroundImage: './src/assets/images/aline-black.png', }, ... }, splash: { image: './src/assets/images/aline-black.png', imageWidth: 200, resizeMode: 'contain', backgroundColor: '#000000', }, plugins: [ [ 'expo-splash-screen', { image: './src/assets/images/aline-black.png', imageWidth: 200, resizeMode: 'contain', backgroundColor: '#000000', }, ], ... ] }프로덕션 빌드된 앱에서 테스트 해보니까 애니메이션 이미지를 원형으로 지정하지 않았는데도,안드로이드에서는 첫번째 사진처럼 원형으로 보이다가 제가 지정한 이미지인 두번째 사진으로 넘어가고 나서 스플래쉬가 사라지는데, 원형으로 나오는 이 기본 동작을 막을 방법이 없을까요?
-
해결됨[왕초보] [누적 5,000명+] Python 프로그래밍 기초 완성 로드맵
순회 중 리스트 수정은 왜 위험한지 궁금합니다.
안녕하세요.열심히 배우고 있는 수강생입니다. 순회 중 리스트 수정은 왜 위험한지 궁금합니다. 좋은 강의 감사합니다.
-
미해결웹 애니메이션을 위한 GSAP 가이드 Part.03
섹션04 Layout에서 Mixed Layout파트의 실습 index.html파일 열었을때 선생님께서 보여주시는 가로스크롤이 안나타남.
안녕하세요. SCROLLTRIGGER-STARTED 폴더에 있는4-5.Mixed Layout의 index.html 파일을 열어section02의 horizontal 주석을 해제하고보았을때 가로 스크롤이 안나타납니다. 제가 뭐 건드린 것은 특별히 없어서finish폴더의 style.css파일과 비교하였는데도가로 스크롤이 안나타납니다. 이상태에서 해당 강의 중 settings.js를 만져서 가로 스크롤이 안나타나도록 확인을 해야하는데 가로스크롤이 안나타난 상태에서강의를 보면서 settings.js파일을 실습하기가 어려울것 같습니다.. 제가 뭘 잘못한게 있을까요?
-
해결됨스프링부트로 직접 만들면서 배우는 대규모 시스템 설계 - 게시판
게시글 조회 최적화 전략 도입 관련, 조회수 원본 데이터와 비교하였을때 원본과 캐싱 데이터 모두 Redis에서 추출하는 데이터임에도 (별도의 key 운용 등) Redis 캐싱 과정을 원본추출 과정과 따로 간주하는 이유(데이터를 가져오는 과정만 보았을때)
학습 관련 질문을 최대한 상세히 남겨주세요!고민 과정도 같이 나열해주셔도 좋습니다.먼저 유사한 질문이 있었는지 검색해보세요.인프런 서비스 운영 관련 문의는 1:1 문의하기를 이용해주세요.안녕하세요 선생님!지난번에 남겨주신 답변내용을 보면서 전략도입의 배경부터 처리과정까지 복기해보았습니다.그러다가 의문이 생긴 점이 있습니다.1) 의문점Request Collapsing, 캐싱중복적재를 제외하고 캐싱하여 데이터를 추출하는 과정만을 보았을때, 원본과 캐싱 데이터 모두 Redis에서 추출해오는 것인데, 원본추출과 비교하였을때 과정적으로 어떠한 차이점이 추가적으로 있있기에 별도의 과정으로 간주하는 것일까? 즉, "캐싱하여 가지고 오는 과정"과 "원본 데이터 추출"을 따로 보고 계셨기에, 어떠한 차이점이 있는지 의문점이 들었습니다. 2) 의문점이 생긴 이유단순하게 간략히 말씀드리자면,조회수 원본데이터는 Redis에서 가져오는 조회수이고, 캐싱해서 가져오는 것 역시 Redis에서 가져오는 조회수로 보여집니다. 원본데이터가 MySQL과 같은 디스크 조회 비용이 큰 저장소에 들어있는 것이 아니라, 동일한 In Memory database인 Redis에서 가져오는 것이기에 성능/비용적으로 캐싱 데이터를 가지고 오는 것에 큰 이점을 느끼지 못하였습니다. 세부적으로 살펴보았을때,ViewClient에서 원본 데이터를 가지고 오는 경우 아래 로직을 통해 Redis에서 추출합니다.articleViewCountRepository.read(articleId);이떄 key는 view::article::#articleId::view_count 입니다.ViewClient에서 Aspect를 처리하여 캐싱 데이터를 가지고 오는 경우 Redis에서 추출합니다.이때 key는 articleViewCount::#articleId입니다.이 과정에 대해 캐싱을 하는 목적을 생각해보았을때(=원본데이터 추출에 시간이 오래 걸릴 경우 성능이 빠른 다른 데이터저장소를 운용하여 이곳에서 데이터를 추출해오기 위함), 동일하게 Redis에서 추출하는 데이터임에에도 key를 별도로 운용하고, 데이터를 추출하는 과정도 다르게 가져가는(간주하는) 이유가 무엇인지 궁금하여 문의코자 합니다(물론 전체 로직을 살펴보았을때는 기존 대비 중복적재/Request Collapsing 과정을 추가하였기에 당연히 성능적인 이점이 존재하겠지만, 데이터를 가져오는 과정 그 자체만을 보았을때는 의문점이 들었습니다). 이게 데이터를 추출하는 과정이 다르기에, 동일한 key로 운용하면 실무적으로 로직이나 key관리방안이 복잡해져서 관리의 효율화를 위해 나누는 것일까요(즉, 기존과 달리 분산락도 사용하고 중복적재를 방지하기 위해 이용하므로 목적 자체가 다르기에 key 포맷 및 캐싱을 별도로 운용하는 것으로 이해하는 것이 적절한지)? 제가 캐싱의 목적 부터 잘못 이해하고 있는 것일 수 있고, 실무적으로 비용/성능적 유리하다는 의미가 무엇인지 잘못 이해하고 있는 것일 수 있다고 생각하고 있습니다. 그렇기에 챗지피티에게 물어보기보다는 실무적으로 경험이 풍부하시고 그만큼 검증된 선생님의 판단이 더 정확하고 궁금하여 질문드리게 되었습니다. 바쁘신데도 항상 성심성의껏 답변해주시는 선생님께 감사의 말씀 드립니다. 이효균 드림.
-
미해결실전에서 바로 써먹는 Kafka 입문
email 발송 로직 관련
consumer 쪽에서 이메일 발송 로직 대신 Thread.sleep(3000) 을 써주셨는데요.이 말은, consumer 쓰레드 자체에서 이메일 보내는 로직을 실행한다고 가정해서 그런거라고 이해했습니다. 개인적으로 consumer 는 message 를 consume 만 하고, 실제 비즈니스 로직 (email send) 는 별개의 쓰레드로 async 하게 동작하는게 더 효율적이라고 생각이 되는데요. email 발송 로직을 별개의 쓰레드로 할 때와 현재처럼 consumer 쓰레드에서 할 때 차이점 및 주의해야할 점 (ex. offset 수동 커밋 등) 이 있을까요?
-
미해결스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
윈도우 gradlew.bat 에러
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.1. 강의 내용과 관련된 질문을 남겨주세요.2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.(자주 하는 질문 링크: https://bit.ly/3fX6ygx)3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.=========================================[질문 템플릿]1. 강의 내용과 관련된 질문인가요? (예/아니오)2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)[질문 내용]여기에 질문 내용을 남겨주세요.프로젝트 지웠다가 처음부터 다시 해보기도 하고 질문글 따라 보면서 해봐도 에러가 떠서 어떻게 해야할지 모르겠어요.. .
-
미해결직장인에게 꼭 필요한 파이썬-아래아한글 자동화 레시피
자간 자동 프로그램 실행 후 원하는 위치에서 실행
자간 자동조절 코드를 제가 좀 수정하여 아래와 같이 작성해 보았습니다만 이렇게 실행을 하니 표가 많은 한글의 경우 시간이 너무 오래걸려 불편한 상황입니다.단순반복인 표의 경우 육안으로 확인하여 일괄 수정하는 것이 빠른경우가 있어 한글 실행 후 특정 커서위치에서 자간 조절 프로그램을 실행하고 싶은데 코드를 어떻게 적용해야 할 지 몰라서 질문 남깁니다.import os from tkinter.filedialog import askopenfilenames import win32com.client as win32 hwp = None def 한글_시작(): """ 아래아한글 시작 함수 """ global hwp hwp = win32.gencache.EnsureDispatch("hwpframe.hwpobject") # HWP 창이 없으면 새 문서를 열어서 창을 만듭니다. if hwp.XHwpWindows.Count == 0: hwp.NewDoc() # 첫 번째 HWP 창이 보이도록 설정합니다. hwp.XHwpWindows.Item(0).Visible = True hwp.RegisterModule("FilePathCheckDLL", "FilePathCheckerModule") return hwp def 파일선택(): """ 파일선택 함수 """ filelist = askopenfilenames(title="수정할 한/글문서를 모두 선택해주세요.", initialdir=os.getcwd(), filetypes=[("한/글파일", "*.hwp *.hwpx")]) return filelist def 현재선택영역_글자수(): """ 선택된 영역의 글자 수를 반환합니다. 자간자동조정 함수에서 라인 끝에 걸쳐진 단어의 앞뒤 길이를 각각 계산하기 위함. """ hwp.InitScan(Range=0xff) # 현재 선택 영역 텍스트 추출 _, text = hwp.GetText() hwp.ReleaseScan() # 개행 문자는 글자 수에서 제외하며, 혹시 선택이 풀렸을 경우 None이 반환될 수 있어 처리 return len(text.replace('\r\n', '')) if text else 0 def 자간자동조정(): """ 모든 라인을 순회하면서 끝에 걸쳐친 단어를 탐색함. 잘린 단어의 앞이 길면 라인 전체의 자간을 줄이고, 잘린 단어의 뒤가 길면 라인 전체의 자간을 늘임. 한 줄 문단이 되거나 걸쳐진 단어가 없으면 종료. """ original_pos = hwp.GetPos() # 현재 커서 위치 저장 (이 라인 시작점) # 현재 라인의 텍스트 길이를 확인하여 빈 라인 또는 한 줄 문단 판단 hwp.Run("MoveLineBegin") hwp.Run("MoveSelLineEnd") current_line_text_length = 현재선택영역_글자수() hwp.Run("Cancel") # 선택 해제 # 원래 위치로 복원 (MoveLineEnd 이동 전에) hwp.SetPos(original_pos[0], original_pos[1], original_pos[2]) # 1. 라인이 비어있는 경우 (Enter만 있는 줄 등) 자간 조정 불필요 if current_line_text_length == 0: return # 2. 현재 라인 끝에 단어가 걸쳐있는지 확인 hwp.Run("MoveLineEnd") # 라인 끝으로 이동 hwp.Run("MoveSelWordBegin") # 라인 끝에서 단어 시작까지 선택 줄_끝_단어_부분_길이 = 현재선택영역_글자수() if 줄_끝_단어_부분_길이 == 0: # 라인 끝에 단어가 없으면 (공백으로 끝나거나 문단 끝) hwp.Run("Cancel") # 선택 해제 return # 자간 조정 불필요 (다음 라인으로 이동은 외부 루프에서 처리) hwp.Run("MoveSelWordEnd") # 단어 전체 선택 단어_전체_길이 = 현재선택영역_글자수() hwp.Run("Cancel") # 선택 해제 # 3. 단어가 라인 끝에 온전히 존재하는 경우 (잘리지 않았다는 의미) if 줄_끝_단어_부분_길이 == 단어_전체_길이: return # 자간 조정 불필요 # 4. 한 줄 문단 판단 (라인 전체 글자수와 단어 전체 글자수가 같은 경우) # (예: "안녕하세요." 와 같이 한 단어가 한 라인을 차지하는 경우) if current_line_text_length == 단어_전체_길이: return # 자간 조정 불필요 # 위 조건들을 모두 통과했다면, 단어가 라인 끝에 걸쳐있다고 판단하고 자간 조정 실행 # 라인 전체를 선택하기 위해 다시 라인 시작으로 이동 후 선택 hwp.Run("MoveLineBegin") hwp.Run("MoveSelLineEnd") # 앞부분길이와 뒷부분길이로 자간 조정 방향 결정 # 여기서는 '줄_끝_단어_부분_길이'가 잘린 단어의 '앞'부분이고 # '단어_전체_길이 - 줄_끝_단어_부분_길이'가 잘린 단어의 '뒷'부분이라고 간주합니다. if 줄_끝_단어_부분_길이 >= (단어_전체_길이 - 줄_끝_단어_부분_길이): # 앞이 길면 hwp.Run("CharShapeSpacingDecrease") # 라인 자간 -1% else: # 뒤가 길면 hwp.Run("CharShapeSpacingIncrease") # 라인 자간 +1% hwp.Run("Cancel") # 선택 해제 # 원래 위치로 복원할 필요 없이, 다음 라인으로의 이동은 외부 루프에서 수행 def 컨트롤_내부_자간조정(): """ 표나 글상자 등 텍스트가 들어가는 모든 영역의 자간을 조정하기 위함 """ area = 2 # 본문(1) 다음부터 컨트롤 영역 시작 while True: original_doc_pos = hwp.GetPos() # 현재 문서 전체 커서 위치 저장 # 컨트롤 영역의 첫 시작으로 이동 (area: 구역번호) hwp.SetPos(area, 0, 0) # 만약 GetPos()[0]가 0이면 더 이상 유효한 컨트롤 영역이 없거나, # 문서의 마지막에 도달하여 본문 영역으로 돌아왔다는 의미 if hwp.GetPos()[0] == 0: break #print(f"컨트롤 영역 {area}번 탐색 시작...") # 디버깅용 while True: current_line_pos = hwp.GetPos() # 현재 컨트롤 내 라인 시작 위치 # 자간자동조정은 단어가 걸쳐있을 때만 실행되도록 수정되었으므로, 매번 호출 자간자동조정() # 다음 라인으로 이동을 시도합니다. hwp.Run("MoveLineEnd") hwp.Run("MoveNextPosEx") # 다음 위치로 이동 후, 현재 위치가 이전 위치와 동일하다면 # 더 이상 이동할 라인/컨트롤이 없다는 의미 if hwp.GetPos() == current_line_pos: #print(f"컨트롤 영역 {area}번 라인 탐색 종료 (더 이상 이동 불가).") # 디버깅용 break # 이 컨트롤 영역 내 라인 탐색 종료 # 만약 MoveNextPosEx로 이동한 결과가 현재 컨트롤 영역을 벗어나 본문으로 진입했거나 # 다른 컨트롤 영역으로 넘어간 경우에도 해당 컨트롤 영역 탐색을 종료합니다. if hwp.GetPos()[0] != area: #print(f"컨트롤 영역 {area}번 라인 탐색 종료 (컨트롤 영역 벗어남).") # 디버깅용 break area += 1 # 다음 컨트롤 영역을 탐색하기 위해 (SetPos가 아닌 Run("NextCtrl")) # 현재 위치가 이미 컨트롤 영역 밖으로 나갔을 수도 있으므로 # 다음 컨트롤로 이동하는 명확한 명령이 필요할 수 있으나, # SetPos(area, 0, 0)이 다음 컨트롤의 시작점을 찾아주므로 유효함. # 컨트롤 탐색 완료 후, 원래 문서 전체 커서 위치로 복귀 (본문 루프에 영향을 주지 않도록) # 하지만 본문 루프에서 컨트롤 내부 자간 조정을 마지막에 호출하므로, 이 복귀는 불필요할 수 있습니다. # hwp.SetPos(original_doc_pos[0], original_doc_pos[1], original_doc_pos[2]) # 일반적으로 컨트롤 작업 후에는 다시 본문 맨 끝으로 가서 종료합니다. # 컨트롤 영역 탐색이 모두 끝나면 문서의 끝으로 이동 hwp.Run("MoveDocEnd") def 끝위치추출(): """ 본문 탐색 while문의 종료 조건으로 "문서 끝에 도착하면 반복종료"를 구현하기 위해 문서 끝 위치를 미리 추출해 둠 """ hwp.Run("MoveDocEnd") end_pos = hwp.GetPos() # 종료위치 저장 hwp.Run("MoveDocBegin") return end_pos if __name__ == '__main__': try: hwp = 한글_시작() 파일목록 = 파일선택() if not 파일목록: # 선택된 파일이 없으면 그냥 종료 print("선택된 파일이 없습니다. 프로그램을 종료합니다.") else: for 파일 in 파일목록: print(f"'{os.path.basename(파일)}' 파일 자간 조정 작업 시작...") hwp.Open(파일, Format="HWP") # 파일 형식 지정 끝위치 = 끝위치추출() # 본문 자간조정 print("본문 자간 조정 작업 시작...") while True: current_pos = hwp.GetPos() # 문서 끝에 도달했는지 확인 (섹션, 문단, 오프셋 모두 비교) if current_pos[0] == 끝위치[0] and \ current_pos[1] == 끝위치[1] and \ current_pos[2] >= 끝위치[2]: # 문서 끝 오프셋에 도달하거나 넘어선 경우 #print("본문 자간 조정 작업: 문서 끝에 도달했습니다.") # 디버깅용 break 자간자동조정() # 수정된 자간자동조정 함수 호출 (필요 없으면 내부에서 스킵) # 다음 라인으로 이동 hwp.Run("MoveLineEnd") hwp.Run("MoveNextPosEx") # 이동 후 현재 위치가 이전 위치와 동일하다면 (더 이상 이동할 곳이 없다는 의미) # 이는 문서의 끝에 도달했지만, GetPos()가 끝위치와 정확히 일치하지 않을 때 발생할 수 있음 if hwp.GetPos() == current_pos: #print("본문 자간 조정 작업: 더 이상 이동할 라인이 없습니다.") # 디버깅용 break # 표 및 글상자 자간조정 print("컨트롤(표, 글상자 등) 내부 자간 조정 작업 시작...") 컨트롤_내부_자간조정() print(f"'{os.path.basename(파일)}' 파일의 자간조정 작업이 끝났습니다!") # 파일 저장 시 원본 파일명 뒤에 "(자간조정)"을 붙여 새로운 파일명 생성 base_name, ext = os.path.splitext(파일) save_path = f"{base_name}(자간조정){ext}" hwp.SaveAs(Path=save_path, Format=hwp.XHwpDocuments.Item(0).Format) hwp.Close(isDirty=False) # 저장했으니 저장 여부 묻지 않고 닫기 print("모든 파일의 자간 조정 작업이 완료되었습니다! 한글 프로그램을 종료합니다. ㅋㅋㅋ") except Exception as e: print(f"오류가 발생했습니다: {e} ㅠㅠ") # 어떤 오류라도 HWP 객체가 있다면 종료를 시도 if hwp is not None: try: hwp.Quit() except Exception as quit_e: print(f"오류 발생 후 한글 종료 중 오류: {quit_e}") finally: # 모든 작업이 완료되거나 오류 발생 시 한글 프로그램을 종료합니다. if hwp is not None: try: hwp.Quit() except Exception as quit_e: print(f"모든 작업 후 한글 종료 중 오류: {quit_e} (이미 종료되었을 수 있습니다.)")
-
미해결React Native with Expo: 제로초에게 제대로 배우기
안드로이드 공개 테스트 앱 관련 질문
제로초님 안녕하세요! 질문이 많네요 위 사진과 같이 공개 테스트 앱을 출시 완료했고, 기기 카탈로그에서도 제가 테스트하는 안드로이드 기기가 지원됨을 확인했습니다. 그런데 Android에서 참여, 웹에서 참여 링크 두개다 접속하면,구글 플레이 스토어 앱이 열리면서 위 사진과 같이 "항목을 찾을 수 없습니다." 라는 메시지만 나오고 앱을 다운로드 할 수 없는 상황입니다 ㅠ해결방법을 아신다면 알려주시면 감사하겠습니다!